飞出JWT,新增FalconClaim用于api的Token认证

This commit is contained in:
falcon 2022-12-07 10:37:23 +08:00
parent ce77072812
commit 2488b15532
29 changed files with 543 additions and 502 deletions

View File

@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Falcon.SugarApi.JWT;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Falcon.SugarApi.Test
{
/// <summary>
/// JwtTokenBuilderTest
/// </summary>
[TestClass]
public class JwtTokenBuilderTest
{
/// <summary>
/// Token获取测试
/// </summary>
[TestMethod]
public void GetTokenTest() {
var loginTime = DateTime.Now;
var playload = new LoginUserInfo {
UserName = "abdc",
LoginTime = loginTime,
Roles = new List<string> { "admin", "user" },
};
if (new JwtTokenBuilder().TryGetToken(playload, out var token, out var exception)) {
Console.WriteLine("token:{0}", token);
}
else {
Console.WriteLine(exception.ToString());
Assert.Fail("获取token失败");
}
Assert.IsNotNull(token);
if (new JwtTokenBuilder().TryGetPlayload(token, out var pl, out var exception1)) {
}
else {
Console.WriteLine(exception1.ToString());
Assert.Fail("获取Playload失败");
}
Assert.IsNotNull(pl);
Assert.AreEqual(playload.UserName, pl.UserName);
Assert.IsTrue(pl.Roles != null);
Assert.IsTrue(pl.Roles.Count == 2);
Assert.IsTrue(pl.Roles.Any(m => m == "admin"));
Assert.IsTrue(pl.Roles.Any(m => m == "user"));
}
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 为swagger openapi接口增加验证选项
/// </summary>
public class AddFalconClaimHeaderFilter:IOperationFilter
{
/// <inheritdoc/>
public void Apply(OpenApiOperation operation,OperationFilterContext context) {
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
//var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter)
// .Any(filter => filter is FalconClaimAuthorizeAttribute);
var metedata = context.ApiDescription.ActionDescriptor.EndpointMetadata;
var isAuthorized = metedata.Any(m => m is FalconClaimAuthorizeAttribute);
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter)
.Any(filter => filter is IAllowAnonymousFilter);
if(isAuthorized && !allowAnonymous) {
operation.Parameters = operation.Parameters ?? new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter {
Name = FalconClaimOption.FalconAuthenticationKey,
In = ParameterLocation.Header,
Description = FalconClaimOption.Description,
Required = true,
Schema = new OpenApiSchema {
Type = "string",
Default = new OpenApiString(FalconClaimOption.TokenPrefix),
},
});
}
}
}
}

View File

@ -0,0 +1,56 @@
using Falcon.SugarApi.Encryption;
using Falcon.SugarApi.JsonSerialize;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 利用AES生成token
/// </summary>
public class AesTokenBuilder:ITokenBuilter
{
/// <summary>
/// 构造基于AES算法的token生成器
/// </summary>
/// <param name="aes">aes算法</param>
/// <param name="factory">json工厂</param>
public AesTokenBuilder(IAESEncryption aes,JsonSerializeFactory factory) {
Aes = aes;
this.Json = factory.CreateJsonSerialize();
}
public IAESEncryption Aes { get; }
public IJsonSerialize Json { get; set; }
/// <inheritdoc/>
public List<Claim>? GetClaims(string token) {
var str = this.Aes.Decrypt(FalconClaimOption.SecKey,token);
if(str.IsNullOrEmpty()) {
return new List<Claim>();
}
return this.Json.Deserialize<List<ClaimSerModel>>(str)
.Select(m => new Claim(m.Type,m.Value)).ToList();
}
/// <inheritdoc/>
public string? GetToken(List<Claim> claims) {
var obj = claims.Select(m => new ClaimSerModel(m.Type,m.Value));
return this.Aes.Encrypt(FalconClaimOption.SecKey,this.Json.Serialize(obj));
}
class ClaimSerModel
{
public ClaimSerModel(string type,string value) {
Type = type;
Value = value;
}
public string Type { get; set; }
public string Value { get; set; }
}
}
}

View File

@ -0,0 +1,23 @@
using System.Security.Claims;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 身份扩展
/// </summary>
public static class ClaimsIdentityExtend
{
/// <summary>
/// 象身份中增加声明
/// </summary>
/// <param name="cid">身份</param>
/// <param name="ClaimTypes">声明类型</param>
/// <param name="value">声明值</param>
/// <returns>身份</returns>
public static ClaimsIdentity AddClaim(this ClaimsIdentity cid,string ClaimTypes,string value) {
cid.AddClaim(new Claim(ClaimTypes,value));
return cid;
}
}
}

View File

@ -0,0 +1,108 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 自定义验证方式
/// </summary>
public class FalconAuthenticationHandler:IAuthenticationHandler
{
/// <summary>
/// 构造自定义身份验证方式
/// </summary>
/// <param name="tokenBuilter">token工具</param>
public FalconAuthenticationHandler(ITokenBuilter tokenBuilter) {
this.Scheme = null;
this.Context = null;
TokenBuilter = tokenBuilter;
}
/// <summary>
/// 验证计划,模式
/// </summary>
public AuthenticationScheme? Scheme { get; set; }
/// <summary>
/// HttpContext 上下文
/// </summary>
public HttpContext? Context { get; set; }
/// <inheritdoc />
public Task<AuthenticateResult> AuthenticateAsync() {
if(!this.Context.Request.Headers.TryGetValue(FalconClaimOption.FalconAuthenticationKey,out var val)) {
return UnLoginResultTask;
}
var token = val.ToString();
if(token.IsNullOrEmpty()) {
return UnLoginResultTask;
}
if(FalconClaimOption.TokenPrefix.IsNotNullOrEmpty() && !token.StartsWith(FalconClaimOption.TokenPrefix)) {
return UnLoginResultTask;
}
try {
var ticket = GetTicket(token);
if(ticket == null) {
return UnLoginResultTask;
}
return Task.FromResult(AuthenticateResult.Success(ticket));
} catch(Exception) {
return UnLoginResultTask;
}
}
/// <inheritdoc />
public Task ChallengeAsync(AuthenticationProperties? properties) {
this.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return Task.CompletedTask;
}
/// <inheritdoc />
public Task ForbidAsync(AuthenticationProperties? properties) {
this.Context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return Task.CompletedTask;
}
/// <inheritdoc />
public Task InitializeAsync(AuthenticationScheme scheme,HttpContext context) {
this.Scheme = scheme; this.Context = context;
return Task.CompletedTask;
}
/// <summary>
/// 根据登录token获取票据
/// </summary>
private AuthenticationTicket? GetTicket(string token) {
token.ThrowNullExceptionWhenNull();
List<Claim>? claims = null;
try {
claims = this.TokenBuilter.GetClaims(token);
} catch(Exception) {
return null;
}
if(claims == null || claims.Count == 0) {
return null;
}
var cid = new ClaimsIdentity(FalconClaimOption.SchemeName);
cid.AddClaims(claims);
var principal = new ClaimsPrincipal(cid);
return new AuthenticationTicket(principal,this.Scheme.Name);
}
/// <summary>
/// 验证失败任务
/// </summary>
//public static Task<AuthenticateResult> UnLoginResultTask => throw new ApiDefinistions.ApiException("未登录");
public static Task<AuthenticateResult> UnLoginResultTask => Task.FromResult(AuthenticateResult.Fail("未登录"));
/// <summary>
/// Token工具
/// </summary>
public ITokenBuilter TokenBuilter { get; }
}
}

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Authorization;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 根据角色验证
/// </summary>
public class FalconClaimAuthorizeAttribute:AuthorizeAttribute
{
/// <summary>
/// 通过提供的角色进行验证
/// </summary>
public FalconClaimAuthorizeAttribute() {
base.AuthenticationSchemes = FalconClaimOption.SchemeName;
}
/// <summary>
/// 通过提供的角色进行验证
/// </summary>
/// <param name="roles">,号分割的角色列表</param>
public FalconClaimAuthorizeAttribute(string roles) {
base.AuthenticationSchemes = FalconClaimOption.SchemeName;
base.Roles = roles;
}
}
}

View File

@ -0,0 +1,40 @@
using System.Collections.Generic;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// FalconClaim验证选项
/// </summary>
public class FalconClaimOption
{
/// <summary>
/// 自定义验证方式名
/// </summary>
public static string SchemeName { get; set; } = "FalconClaimSchemeName";
/// <summary>
/// 用于提供验证token的键
/// </summary>
public static string FalconAuthenticationKey { get; set; } = "Falcon_WWW_Authenticate";
/// <summary>
/// 用于加密的key
/// </summary>
public static string SecKey { get; set; } = "E9319792CB7249AD8E432000E9F2FE7A";
/// <summary>
/// 验证头说明
/// </summary>
public static string Description { get; set; } = "FalconClaim验证的Token。";
/// <summary>
/// 请求Token前缀
/// </summary>
public static string TokenPrefix { get; set; } = "";
/// <summary>
/// 二次认证方法
/// </summary>
public List<IUserValidate> UserValidates { get; set; } = new List<IUserValidate>();
}
}

View File

@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 扩展IApplicationBuilder
/// </summary>
public static class IApplicationBuilderExtend
{
/// <summary>
/// 使用认证UseAuthentication和授权UseAuthorization。需要加在在UseRouting后UseEndpoints前
/// </summary>
public static IApplicationBuilder UseFalconClaim(this IApplicationBuilder app) {
app.UseAuthentication().UseAuthorization();
return app;
}
}
}

View File

@ -0,0 +1,33 @@
using Falcon.SugarApi.Encryption;
using Falcon.SugarApi.JsonSerialize;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.Text.Json;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 扩展IServiceCollection
/// </summary>
public static class IServiceCollectionExtend
{
/// <summary>
/// 增加FalconCalom相关服务
/// </summary>
public static IServiceCollection AddFalconClaim(this IServiceCollection services,Action<FalconClaimOption>? optionBuilder = null) {
optionBuilder?.Invoke(new FalconClaimOption());
services.TryAddSingleton<JsonSerializeFactory>();
services.TryAddSingleton<ITokenBuilter,AesTokenBuilder>();
services.TryAddSingleton<AESConfig>();
services.TryAddSingleton<IAESEncryption,AESProvider>();
services.AddAuthentication(b => {
b.DefaultAuthenticateScheme = FalconClaimOption.SchemeName;
b.DefaultChallengeScheme = FalconClaimOption.SchemeName;
b.DefaultForbidScheme = FalconClaimOption.SchemeName;
b.AddScheme<FalconAuthenticationHandler>(FalconClaimOption.SchemeName,FalconClaimOption.SchemeName);
});
return services;
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Security.Claims;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 通过声明生成token
/// </summary>
public interface ITokenBuilter
{
/// <summary>
/// 根据声称组生成token
/// </summary>
/// <param name="claims">一组声明</param>
/// <returns>token</returns>
string? GetToken(List<Claim> claims);
/// <summary>
/// 通过token获取声明组
/// </summary>
/// <param name="token">token</param>
/// <returns>声明组</returns>
List<Claim>? GetClaims(string token);
}
}

View File

@ -0,0 +1,16 @@
using System.Security.Claims;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 认证时候提供二次验证方法
/// </summary>
public interface IUserValidate
{
/// <summary>
/// 在认证时候提供二次认证的方法
/// </summary>
/// <param name="identity">身份</param>
void UserLogin(ClaimsIdentity identity);
}
}

View File

@ -0,0 +1,16 @@
using System.Security.Claims;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 名字声明
/// </summary>
public class NameClaim:Claim
{
/// <summary>
/// 提供名字实现名字声明
/// </summary>
/// <param name="name">名字</param>
public NameClaim(string name) : base(ClaimTypes.Name,name) { }
}
}

View File

@ -0,0 +1,7 @@
FalconClaim 使用说明:
1. 用户登录模块提供了UserLoginModel和UserLoginResult类分别负责提供登录凭据和登录结果可以扩展使用当然也可以使用自己的模型类。
> 1.通过UserLoginModel提供的凭据验证用户合法性。
> 2.通过ITokenBuilter.GetToken方法获取登录token。该方法需要一个Claim列表该列表至少应包含一个name和role声明表示登录的用户名和角色也可以根据需要增加其他声明。
> 3.构造UserLoginResult对象返回token。

View File

@ -0,0 +1,18 @@
using System.Security.Claims;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 安全标识声明
/// </summary>
public class SidClaim:Claim
{
/// <summary>
/// 提供名字实现名字声明
/// </summary>
/// <param name="sid">安全标识</param>
public SidClaim(string sid) : base(ClaimTypes.Sid,sid) { }
}
}

View File

@ -0,0 +1,51 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// SwaggerGenOptions扩展
/// </summary>
public static class SwaggerGenOptionsExtend
{
/// <summary>
/// 在swaggerUI中为有FalconClaimAuthorizeAttribute特性的Action增加token头
/// </summary>
/// <param name="options">SwaggerGenOptions</param>
/// <returns>SwaggerGenOptions</returns>
public static SwaggerGenOptions AddHeaderForFalconClaim(this SwaggerGenOptions options) {
options.OperationFilter<AddFalconClaimHeaderFilter>();
return options;
}
/// <summary>
/// 在swaggerUI中为所有api请求增加token头
/// </summary>
/// <param name="options">SwaggerGenOptions</param>
/// <returns>SwaggerGenOptions</returns>
public static SwaggerGenOptions AddHeaderForAllApi(this SwaggerGenOptions options) {
options.AddSecurityDefinition(FalconClaimOption.SchemeName,new OpenApiSecurityScheme {
Description = FalconClaimOption.Description,
Name = FalconClaimOption.FalconAuthenticationKey,
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = FalconClaimOption.SchemeName,
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id =FalconClaimOption.SchemeName,
}
},
new List<string>()
}
});
return options;
}
}
}

View File

@ -0,0 +1,18 @@
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 用户登录凭据模板
/// <para>用于验证用户登录,可以实现自己的类进行验证</para>
/// </summary>
public class UserLoginModel
{
/// <summary>
/// 用户名
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string? Password { get; set; }
}
}

View File

@ -0,0 +1,44 @@
using System;
namespace Falcon.SugarApi.FalconClaim
{
/// <summary>
/// 用户登录凭据模板
/// <para>登录结果Token作为票据返回。</para>
/// </summary>
public class UserLoginResult
{
/// <summary>
/// 登录结果。成功返回True失败返回False
/// </summary>
public bool Success { get; set; } = false;
/// <summary>
/// 登录结果信息
/// </summary>
public string? Msg { get; set; }
/// <summary>
/// 登录票据Token
/// </summary>
public string? Token { get; set; }
/// <summary>
/// 登录时间。默认当前系统时间
/// </summary>
public DateTime? LoginTime { get; set; } = DateTime.Now;
/// <summary>
/// 获取一条成功的登录信息
/// </summary>
/// <param name="token">成功登录Token</param>
/// <returns>成功登录结果</returns>
public static UserLoginResult LoginSuccess(string token)
=> new UserLoginResult { Success = true,Token = token,Msg = "ok" };
/// <summary>
/// 登录失败。
/// </summary>
/// <param name="msg">失败原因</param>
/// <returns>失败的登录结果</returns>
public static UserLoginResult LoginFail(string? msg)
=> new UserLoginResult { Success = false,Msg = msg };
}
}

View File

@ -1,42 +0,0 @@
using Falcon.SugarApi.Encryption;
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// 使用AES算法生成Token
/// </summary>
public class AESTokenbuilder : IJwtTokenBuilder
{
/// <summary>
/// AES加密算法
/// </summary>
public IAESEncryption AES { get; set; }
/// <summary>
/// jwt生成参数
/// </summary>
public JwtContext Jwt { get; }
/// <summary>
/// 构造AES的Token
/// </summary>
/// <param name="config">AES参数</param>
/// <param name="jwt">jwt生成参数</param>
public AESTokenbuilder(AESConfig config, JwtContext jwt) {
Jwt = jwt;
AES = new AESProvider(config);
}
/// <inheritdoc>/
public LoginUserInfo GetPlayload(string token) {
token.ThrowNullExceptionWhenNull();
return this.AES.Decrypt<LoginUserInfo>(Jwt.SecKey, token);
}
/// <inheritdoc>/
public string GetToken(LoginUserInfo playload) {
playload.ThrowNullExceptionWhenNull();
return this.AES.Encrypt(Jwt.SecKey, playload);
}
}
}

View File

@ -1,73 +0,0 @@
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// 验证
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ApiAuthorizationAttribute : Attribute, IAuthorizationFilter
{
/// <summary>
/// 用户需要具有的角色
/// </summary>
public string[] Roles { get; set; }
/// <summary>
/// 只需要登录即可通过验证
/// </summary>
public ApiAuthorizationAttribute() {
this.Roles = new string[] { };
}
/// <summary>
/// 需要登录并且具有一定的角色
/// </summary>
/// <param name="roles">角色组</param>
public ApiAuthorizationAttribute(params string[] roles) {
this.Roles = roles;
}
/// <inheritdoc/>
public void OnAuthorization(AuthorizationFilterContext context) {
if (context.Filters.Any(f => f is IAllowAnonymousFilter)) {
return;
}
var option = context.HttpContext.RequestServices.GetRequiredService<JwtContext>();
var key = option?.AuthHeaderKey;
key.ThrowNullExceptionWhenNull();
var token = context.HttpContext.Request.Headers[key].ToString();
if (token.IsNullOrEmpty()) {
Unauthorized(context);
return;
}
var jwt = option?.JwtTokenBuilder;
jwt.ThrowNullExceptionWhenNull();
var user = jwt.GetPlayload(token);
var userLogin = option?.UserLogin;
if (userLogin != null && !userLogin.CheckUserLogin(user)) {
Unauthorized(context);
return;
}
if (this.Roles != null && this.Roles.Length > 0 && !userLogin.UserInRoles(user, this.Roles)) {
Unauthorized(context);
return;
}
return;
}
/// <summary>
/// 返回授权失败
/// </summary>
/// <param name="context">上下文</param>
private static void Unauthorized(AuthorizationFilterContext context) {
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
}

View File

@ -1,22 +0,0 @@
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// jwt token 生成器
/// </summary>
public interface IJwtTokenBuilder
{
/// <summary>
/// 获取Token负载
/// </summary>
/// <param name="token">登录Token</param>
/// <returns>负载信息</returns>
LoginUserInfo GetPlayload(string token);
/// <summary>
/// 获取Token
/// </summary>
/// <param name="playload">负载</param>
/// <returns>Token对象</returns>
string GetToken(LoginUserInfo playload);
}
}

View File

@ -1,56 +0,0 @@
using Falcon.SugarApi.Encryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// 服务扩展
/// </summary>
public static class IServiceCollectionExtend
{
/// <summary>
/// 注册Falcon.SqlSugar.JWT
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddFalconJWT(this IServiceCollection services)
=> services.AddFalconJWT(null);
/// <summary>
/// 注册Falcon.SqlSugar.JWT.JwtContext并配置相关参数
/// <para>消费端通过JwtContext注入</para>
/// </summary>
/// <param name="services">服务集合</param>
/// <param name="OptionBuilder">参数创建器</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddFalconJWT(this IServiceCollection services, Action<JwtContext>? OptionBuilder) {
services.AddSingleton<JwtContext>(sp => {
var option = new JwtContext();
var jtb = sp.GetService<IJwtTokenBuilder>();
if (jtb == null) {
var aesc = sp.GetService<AESConfig>() ?? new AESConfig();
jtb = new AESTokenbuilder(aesc, option);
}
option.JwtTokenBuilder = jtb;
var ulj = sp.GetService<IUserLogin>();
if (ulj != null) {
option.UserLogin = ulj;
}
OptionBuilder?.Invoke(option);
if (option.UserLogin == null) {
throw new Exception("必须为JwtOptions提供UserLogin属性");
}
if (option.JwtTokenBuilder == null) {
throw new Exception("必须为JwtOptions提供JwtTokenBuilder属性");
}
return option;
});
return services;
}
}
}

View File

@ -1,39 +0,0 @@
using System.Collections.Generic;
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// 用户登录验证接口
/// </summary>
public interface IUserLogin
{
/// <summary>
/// 登录是否成功
/// </summary>
/// <param name="login">登录凭据</param>
/// <returns>成功返回用户信息否则返回null</returns>
LoginUserInfo? Login(LoginDto login);
/// <summary>
/// 用户登出
/// </summary>
/// <param name="userInfo">用户名</param>
/// <returns>True成功False失败</returns>
bool LogOut(LoginUserInfo userInfo);
/// <summary>
/// 登录用户验证。检查客户端提供的已登录用户是否仍然处于登录中。
/// </summary>
/// <param name="userInfo">登录凭据</param>
/// <returns>登录中返回True否则返回False</returns>
bool CheckUserLogin(LoginUserInfo userInfo);
/// <summary>
/// 用户是否具有某个角色
/// </summary>
/// <param name="userInfo">用户信息</param>
/// <param name="roles">需要具有的角色组</param>
/// <returns>True具有,False不具有</returns>
bool UserInRoles(LoginUserInfo userInfo, params string[] roles);
}
}

View File

@ -1,29 +0,0 @@
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// JWT认证上下文
/// </summary>
public class JwtContext
{
/// <summary>
/// 验证头的键值
/// </summary>
public string AuthHeaderKey { get; set; } = "auth";
/// <summary>
/// 用户Token加密的秘钥
/// </summary>
public string SecKey { get; set; } = "fefafwefwf464664f64e64f63";
/// <summary>
/// jwtToken生成器
/// </summary>
public IJwtTokenBuilder? JwtTokenBuilder { get; set; }
/// <summary>
/// 用户登录管理
/// </summary>
public IUserLogin? UserLogin { get; set; }
}
}

View File

@ -1,64 +0,0 @@
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Microsoft.AspNetCore.Server.IIS.Core;
using System;
using System.Text;
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// jwt token 生成器
/// </summary>
public class JwtTokenBuilder : IJwtTokenBuilder
{
/// <summary>
/// 使用默认配置实例化
/// </summary>
public JwtTokenBuilder() : this(new JwtContext()) { }
/// <summary>
/// 实例化
/// </summary>
/// <param name="options">JWT认证配置</param>
public JwtTokenBuilder(JwtContext options) {
Options = options;
}
/// <summary>
/// JWT认证配置
/// </summary>
public JwtContext Options { get; }
/// <summary>
/// 获取Token
/// </summary>
/// <param name="playload">负载</param>
/// <returns>Token对象</returns>
public string GetToken(LoginUserInfo playload) {
playload = playload ?? throw new ArgumentNullException(nameof(playload));
var key = Encoding.UTF8.GetBytes(this.Options.SecKey);
var algorithm = new HMACSHA256Algorithm();
var serializer = new JsonNetSerializer();
var urlEncoder = new JwtBase64UrlEncoder();
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
return encoder.Encode(playload, key);
}
/// <summary>
/// 获取Token负载
/// </summary>
/// <param name="token">登录Token</param>
/// <returns>负载信息</returns>
public LoginUserInfo GetPlayload(string token) {
var key = Encoding.UTF8.GetBytes(this.Options.SecKey);
var serializer = new JsonNetSerializer();
var dtProvider = new UtcDateTimeProvider();
var validator = new JwtValidator(serializer, dtProvider);
var urlEncoder = new JwtBase64UrlEncoder();
var algorithm = new HMACSHA256Algorithm();
var decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
return decoder.DecodeToObject<LoginUserInfo>(token, key, true);
}
}
}

View File

@ -1,52 +0,0 @@
using System;
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// JwtTokenBuilder扩展
/// </summary>
public static class JwtTokenBuilderExtend
{
/// <summary>
/// 尝试获取Token
/// </summary>
/// <param name="builder">生成器</param>
/// <param name="userInfo">用户信息</param>
/// <param name="token">生成的token</param>
/// <param name="exception">失败异常</param>
/// <returns>成功True失败False</returns>
public static bool TryGetToken(this JwtTokenBuilder builder, LoginUserInfo userInfo, out string? token, out Exception? exception) {
try {
token = builder.GetToken(userInfo);
exception = null;
return true;
}
catch (Exception ex) {
token = null;
exception = ex;
return false;
}
}
/// <summary>
/// 尝试获取用户信息
/// </summary>
/// <param name="builder">jwt创建器</param>
/// <param name="token">token</param>
/// <param name="userInfo">用户信息</param>
/// <param name="exception">异常</param>
/// <returns>True成功False失败</returns>
public static bool TryGetPlayload(this JwtTokenBuilder builder, string token, out LoginUserInfo? userInfo, out Exception? exception) {
try {
userInfo = builder.GetPlayload(token);
exception = null;
return true;
}
catch (Exception ex) {
userInfo = null;
exception = ex;
return false;
}
}
}
}

View File

@ -1,17 +0,0 @@
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// 登录信息
/// </summary>
public class LoginDto
{
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
}
}

View File

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// JWT载荷信息
/// </summary>
public class LoginUserInfo
{
/// <summary>
/// 登录用户名
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// 用户角色
/// </summary>
public List<string> Roles { get; set; } = new List<string>();
/// <summary>
/// 其他扩展用户信息
/// </summary>
public Dictionary<string, object> UserInfoExtend { get; set; } = new Dictionary<string, object>();
/// <summary>
/// 登录时间
/// </summary>
public DateTime? LoginTime { get; set; }
}
}

View File

@ -1,21 +0,0 @@
namespace Falcon.SugarApi.JWT
{
/// <summary>
/// 登录凭据
/// </summary>
public class TokenDto
{
/// <summary>
/// 登录结果
/// </summary>
public bool Success { get; set; }
/// <summary>
/// 信息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 凭据
/// </summary>
public string Token { get; set; }
}
}

View File

@ -1,7 +0,0 @@
初始化过程:
1. 初始化相关表:
2. 初始化用户系统:检查是否存在用户,如果没有用户插入默认管理员用户,后期可以删除。
2. 初始化角色系统:
> 1. 检查系统所有控制器查询是否标注ApiExplorerSettings特性如果标注查询是否定义GroupName不为空则列为角色之一。
> 2. 检查角色表,如果发现缺少某角色则插入。检查是否存在超管角色,如果不存在则插入