增加api接口jwt认证逻辑

This commit is contained in:
Gui.H 2021-09-09 14:47:49 +08:00
parent f4c4c15736
commit f2af2bb4f6
13 changed files with 305 additions and 32 deletions

View File

@ -0,0 +1,87 @@
using FastTunnel.Api.Models;
using FastTunnel.Core.Client;
using FastTunnel.Core.Config;
using FastTunnel.Server.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace FastTunnel.Api.Controllers
{
public class AccountController : BaseController
{
IOptionsMonitor<DefaultServerConfig> serverOptionsMonitor;
public AccountController(IOptionsMonitor<DefaultServerConfig> optionsMonitor)
{
serverOptionsMonitor = optionsMonitor;
}
/// <summary>
/// 获取Token
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public ApiResponse GetToken(GetTokenRequest request)
{
if ((serverOptionsMonitor.CurrentValue?.Api?.Accounts?.Length ?? 0) == 0)
{
ApiResponse.errorCode = ErrorCodeEnum.NoAccount;
ApiResponse.errorMessage = "账号或密码错误";
return ApiResponse;
}
var account = serverOptionsMonitor.CurrentValue.Api.Accounts.FirstOrDefault((x) =>
{
return x.Name.Equals(request.name) && x.Password.Equals(request.password);
});
if (account == null)
{
ApiResponse.errorCode = ErrorCodeEnum.NoAccount;
ApiResponse.errorMessage = "账号或密码错误";
return ApiResponse;
}
// 生成Token
var claims = new[] {
new Claim("Name", account.Name)
};
ApiResponse.data = GenerateToken(
claims,
serverOptionsMonitor.CurrentValue.Api.JWT.IssuerSigningKey,
serverOptionsMonitor.CurrentValue.Api.JWT.Expires,
serverOptionsMonitor.CurrentValue.Api.JWT.ValidIssuer,
serverOptionsMonitor.CurrentValue.Api.JWT.ValidAudience);
return ApiResponse;
}
public static string GenerateToken(
IEnumerable<Claim> claims, string Secret, int expiresMinutes = 60, string issuer = null, string audience = null)
{
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var securityToken = new JwtSecurityToken(
issuer: issuer,
audience: audience,
claims: claims,
expires: DateTime.Now.AddMinutes(expiresMinutes),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(securityToken);
}
}
}

View File

@ -1,4 +1,5 @@
using FastTunnel.Server.Models; using FastTunnel.Server.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System; using System;
@ -8,6 +9,7 @@ using System.Threading.Tasks;
namespace FastTunnel.Api.Controllers namespace FastTunnel.Api.Controllers
{ {
[Authorize]
[Route("api/[controller]/[action]")] [Route("api/[controller]/[action]")]
[ApiController] [ApiController]
public class BaseController : ControllerBase public class BaseController : ControllerBase

View File

@ -8,6 +8,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.12.2" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.12.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,38 @@
using FastTunnel.Server.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Api.Filters
{
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
readonly ILogger<CustomExceptionFilterAttribute> _logger;
public CustomExceptionFilterAttribute(ILogger<CustomExceptionFilterAttribute> logger)
{
_logger = logger;
}
public override void OnException(ExceptionContext context)
{
_logger.LogError(context.Exception, "【全局异常捕获】");
var res = new ApiResponse()
{
errorCode = ErrorCodeEnum.Exception,
data = null,
errorMessage = context.Exception.Message,
};
var result = new JsonResult(res) { StatusCode = 200 };
context.Result = result;
context.ExceptionHandled = true;
}
}
}

View File

@ -21,5 +21,11 @@ namespace FastTunnel.Server.Models
public enum ErrorCodeEnum public enum ErrorCodeEnum
{ {
NONE = 0, NONE = 0,
AuthError = 1,
Exception = 2,
NoAccount = 3,
} }
} }

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Api.Models
{
public class GetTokenRequest
{
[Required]
public string name { get; set; }
[Required]
public string password { get; set; }
}
}

View File

@ -7,54 +7,58 @@
"Microsoft.Hosting.Lifetime": "Information" "Microsoft.Hosting.Lifetime": "Information"
} }
}, },
//
"EnableFileLog": false, "EnableFileLog": false,
"ClientSettings": { "ClientSettings": {
"Server": { "Server": {
// // [] urls
"Protocol": "ws", // ws(http)wss(https) // ws(http)wss(https)
// ip/ "Protocol": "ws",
// [] ip/urls
"ServerAddr": "test.cc", "ServerAddr": "test.cc",
// // [] urls
"ServerPort": 1270 "ServerPort": 1270
}, },
// Token // []Token
"Token": "TOKEN_FOR_CLIENT_AUTHENTICATION", "Token": "TOKEN_FOR_CLIENT_AUTHENTICATION",
/** /**
* 访 * [] web
* 穿TCP
*/ */
"Webs": [ "Webs": [
{ {
// ip // [] ip
"LocalIp": "127.0.0.1", "LocalIp": "127.0.0.1",
// // []
"LocalPort": 8090, "LocalPort": 8090,
// , 访url http://{SubDomain}.{WebDomain}:{WebProxyPort}/ // [] , 访url http://${SubDomain}.${WebDomain}:${ServerPort}
"SubDomain": "test" // test.test.cc "SubDomain": "test"
// CNAMEA // [] CNAMEA
// "WWW": [ "www.abc.com", "test111.test.cc" ] // "WWW": [ "www.abc.com", "test111.test.cc" ]
} }
], ],
/** /**
* * []
* 穿TCP * 穿TCP
* linux#ssh -oPort=12701 {root}@{ServerAddr} ServerAddr iproot * linux#ssh -oPort=12701 {root}@{ServerAddr} ServerAddr iproot
* 访访 * 访访
*/ */
"Forwards": [ "Forwards": [
{ {
// [] ip
"LocalIp": "127.0.0.1", "LocalIp": "127.0.0.1",
"LocalPort": 3389, // windows3389 // [] windows3389
"RemotePort": 1274 // 访 ip:1274 window "LocalPort": 3389,
// [] 访 ip:1274 window
"RemotePort": 1274
}, },
{ {
"LocalIp": "127.0.0.1", "LocalIp": "127.0.0.1",
"LocalPort": 8090, // windows3389 "LocalPort": 3306, // mysql
"RemotePort": 1275 // 访 ip:1274 window "RemotePort": 1275 // 访 ip:1275 mysql
} }
] ]
} }

View File

@ -14,5 +14,34 @@ namespace FastTunnel.Core.Config
public bool EnableForward { get; set; } = false; public bool EnableForward { get; set; } = false;
public string Token { get; set; } public string Token { get; set; }
public ApiOptions Api { get; set; }
public class ApiOptions
{
public JWTOptions JWT { get; set; }
public Account[] Accounts { get; set; }
}
public class JWTOptions
{
public int ClockSkew { get; set; }
public string ValidAudience { get; set; }
public string ValidIssuer { get; set; }
public string IssuerSigningKey { get; set; }
public int Expires { get; set; }
}
public class Account
{
public string Name { get; set; }
public string Password { get; set; }
}
} }
} }

View File

@ -16,6 +16,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.9" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0-preview.4.21253.7" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0-preview.4.21253.7" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="6.0.0-preview.4.21253.7" /> <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="6.0.0-preview.4.21253.7" />

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DeleteExistingFiles>False</DeleteExistingFiles>
<ExcludeApp_Data>False</ExcludeApp_Data>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net5.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
</PropertyGroup>
</Project>

View File

@ -1,17 +1,21 @@
using FastTunnel.Core; using FastTunnel.Core;
using FastTunnel.Core.Config; using FastTunnel.Core.Extensions;
using FastTunnel.Server.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens;
using System;
using FastTunnel.Core.Config;
using System.Text;
#if DEBUG #if DEBUG
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
#endif #endif
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Server namespace FastTunnel.Server
{ {
@ -27,6 +31,43 @@ namespace FastTunnel.Server
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var serverOptions = Configuration.GetSection("FastTunnel").Get<DefaultServerConfig>();
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(serverOptions.Api.JWT.ClockSkew),
ValidateIssuerSigningKey = true,
ValidAudience = serverOptions.Api.JWT.ValidAudience,
ValidIssuer = serverOptions.Api.JWT.ValidIssuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(serverOptions.Api.JWT.IssuerSigningKey))
};
options.Events = new JwtBearerEvents
{
OnChallenge = async context =>
{
context.HandleResponse();
context.Response.ContentType = "application/json;charset=utf-8";
context.Response.StatusCode = StatusCodes.Status200OK;
await context.Response.WriteAsync(new ApiResponse
{
errorCode = ErrorCodeEnum.AuthError,
errorMessage = context.Error ?? "Token is Required"
}.ToJson());
},
};
});
services.AddAuthorization();
services.AddControllers(); services.AddControllers();
#if DEBUG #if DEBUG
@ -37,8 +78,8 @@ namespace FastTunnel.Server
#endif #endif
// -------------------FastTunnel STEP1 OF 3------------------ // -------------------FastTunnel STEP1 OF 3------------------
services.AddFastTunnelServer(Configuration.GetSection("ServerSettings")); services.AddFastTunnelServer(Configuration.GetSection("FastTunnel"));
// -------------------FastTunnel STEP1 END-------------------- // -------------------FastTunnel STEP1 END-------------------
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -55,16 +96,22 @@ namespace FastTunnel.Server
// -------------------FastTunnel STEP2 OF 3------------------ // -------------------FastTunnel STEP2 OF 3------------------
app.UseFastTunnelServer(); app.UseFastTunnelServer();
// -------------------FastTunnel STEP2 END-------------------- // -------------------FastTunnel STEP2 END-------------------
app.UseRouting(); app.UseRouting();
// --------------------- Custom UI ----------------
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
// --------------------- Custom UI ----------------
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();
// -------------------FastTunnel STEP3 OF 3------------------ // -------------------FastTunnel STEP3 OF 3------------------
endpoints.MapFastTunnelServer(); endpoints.MapFastTunnelServer();
// -------------------FastTunnel STEP3 END-------------------- // -------------------FastTunnel STEP3 END-------------------
}); });
} }
} }

View File

@ -8,19 +8,41 @@
} }
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"urls": "http://*:1270;https://*:4443;", // Http& // Http&
"urls": "http://*:1270",
//
"EnableFileLog": false, "EnableFileLog": false,
"ServerSettings": { "FastTunnel": {
// //
// SubDomain ${SubDomain}.${WebDomain}访访
"WebDomain": "test.cc", "WebDomain": "test.cc",
// 访ip访 // 访访ip
"WebAllowAccessIps": [ "192.168.0.101" ], "WebAllowAccessIps": [ "192.168.0.101" ],
// SSHForward.false // Forward.false
"EnableForward": true, "EnableForward": true,
// Token, // Token
"Token": "TOKEN_FOR_CLIENT_AUTHENTICATION" "Token": "TOKEN_FOR_CLIENT_AUTHENTICATION",
/**
* 访apiJWT
*/
"Api": {
"JWT": {
"ClockSkew": 10,
"ValidAudience": "https://suidao.io",
"ValidIssuer": "FastTunnel",
"IssuerSigningKey": "This is IssuerSigningKey",
"Expires": 120
},
"Accounts": [
{
"Name": "admin",
"Password": "admin123"
}
]
}
} }
} }

View File

@ -0,0 +1 @@
index