概念
集成ASP.NET Core authorization JWT
引用阿里云包
项目文件总览
定义Jwt授权策略处理器
/// <summary>
/// 定义Jwt授权策略处理器
/// </summary>
internal class JwtAuthorizationHandler : AuthorizationHandler<JwtAuthorizationRequirement>
{
readonly IServiceProvider _serviceProvider;
readonly IHttpContextAccessor _httpContextAccessor;
readonly JwtAuthorizationOptions _jwtAuthorizationOptions;
public JwtAuthorizationHandler(IHttpContextAccessor httpContextAccessor, IServiceProvider serviceProvider, IOptions<JwtAuthorizationOptions> jwtAuthorizationOptions)
{
_httpContextAccessor = httpContextAccessor;
_serviceProvider = serviceProvider;
_jwtAuthorizationOptions = jwtAuthorizationOptions.Value;
}
/// <summary>
/// Makes a decision if authorization is allowed.
/// </summary>
/// <param name="context"></param>
/// <param name="requirement"></param>
/// <returns></returns>
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, JwtAuthorizationRequirement requirement)
{
var authResult = await _httpContextAccessor.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
if (authResult.Succeeded && authResult.Principal.Identity.IsAuthenticated)
{
if (_jwtAuthorizationOptions.HandleAuthorization != null)
{
if (await _jwtAuthorizationOptions.HandleAuthorization.Invoke((_serviceProvider, _httpContextAccessor.HttpContext, context)))
{
context.Succeed(requirement);
return;
}
}
context.Succeed(requirement);
return;
}
context.Fail();
}
}
JwtAuthorizationOptions
public class JwtAuthorizationOptions
{
/// <summary>
/// 秘钥
/// </summary>
public string SecurityKey { get; set; }
/// <summary>
/// 令牌过期时间(单位:分钟)
/// </summary>
public double ExpiredMinutes { get; set; }
/// <summary>
/// 是否验证过期时间
/// </summary>
public bool ValidateLifetime { get; set; } = true;
/// <summary>
/// 过期时间容错值(单位:秒)
/// </summary>
public int ClockSkewSecond { get; set; } = 5;
/// <summary>
/// 签发方
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 签收方
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 系统授权通过时,进行下一步自定义业务授权处理
/// </summary>
public Func<(IServiceProvider ServiceProvider, HttpContext HttpContext, AuthorizationHandlerContext AuthorizationHandlerContext), Task<bool>> HandleAuthorization { get; set; }
}
JwtAuthorizationRequirement
/// <summary>
/// 定义Jwt授权策略
/// </summary>
internal class JwtAuthorizationRequirement: IAuthorizationRequirement
{
}
JwtBearerService
public class JwtBearerService : IJwtService
{
private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly JwtAuthorizationOptions _options;
public JwtBearerService(IHttpContextAccessor httpContextAccessor, IOptions<JwtAuthorizationOptions> options)
{
_jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
_httpContextAccessor = httpContextAccessor;
_options = options.Value;
}
#region 创建令牌
/// <summary>
/// 创建令牌
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="identity"></param>
/// <param name="claims"></param>
/// <returns></returns>
public virtual JwtInfo CreateAccessToken<T>(T identity, Dictionary<string, object> claims = null) where T : UserIdentityBase
{
var authUtc = DateTime.UtcNow;
//过期时间
var expiresUtc = authUtc.AddMinutes(_options.ExpiredMinutes);
claims ??= new Dictionary<string, object>();
claims[ClaimTypes.Expiration] = expiresUtc.ToString();
var jwtToken = new JwtSecurityToken
(
_options.Issuer,
_options.Audience,
identity.CreateClaims(claims),
authUtc,
expiresUtc,
new SigningCredentials
(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.SecurityKey)),
SecurityAlgorithms.HmacSha256
)
);
var accessToken = _jwtSecurityTokenHandler.WriteToken(jwtToken);
var jwt = new JwtInfo()
{
AccessToken = accessToken,
AccessTokenUtcExpires = expiresUtc,
AuthUtc = authUtc,
UserId = identity.Id
};
return jwt;
}
#endregion
#region 获取当前上下文用户申明信息
/// <summary>
/// 获取当前上下文用户申明信息
/// </summary>
/// <returns></returns>
public virtual T GetHttpContextUserIdentity<T>() where T : UserIdentityBase
{
T result = default;
if (_httpContextAccessor?.HttpContext?.User?.Identity?.IsAuthenticated ?? false)
result = ((ClaimsIdentity)_httpContextAccessor.HttpContext.User.Identity).ToObject<T>();
return result;
}
#endregion
}
JwtTokenTransferMiddleware
internal class JwtTokenTransferMiddleware
{
private readonly RequestDelegate _next;
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
public JwtTokenTransferMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
var token = HttpContextHelper.QueryHeader<string>(BPAHeader.AuthorizationHeaderKey);
if (!token.HasVal())
token = HttpContextHelper.QueryString<string>(BPAHeader.AuthorizationTokenUrlKey);
if (token.HasVal())
{
if (!token.StartsWith(BPAHeader.JwtBearerAuthenticationScheme))
token = BPAHeader.JwtBearerAuthenticationScheme + token;
if(IsCanReadToken(token))
context.Request.Headers[BPAHeader.AuthorizationHeaderKey] = token;
}
}
finally
{
await _next(context);
}
}
/// <summary>
/// Token是否是符合要求的标准 Json Web 令牌
/// </summary>
/// <param name="tokenStr"></param>
/// <returns></returns>
private bool IsCanReadToken(string tokenStr)
{
if (string.IsNullOrWhiteSpace(tokenStr) || tokenStr.Length < 7)
return false;
if (!tokenStr.Substring(0, 6).Equals(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme))
return false;
tokenStr = tokenStr.Substring(7);
bool isCan = jwtSecurityTokenHandler.CanReadToken(tokenStr);
var jwt = jwtSecurityTokenHandler.ReadJwtToken(tokenStr);
return isCan;
}
}
AuthorizationResultTransformer(授权结果转换)
/// <summary>
/// 授权结果转换器
/// </summary>
internal class AuthorizationResultTransformer : IAuthorizationMiddlewareResultHandler
{
private readonly IAuthorizationMiddlewareResultHandler _handler;
public AuthorizationResultTransformer()
{
_handler = new AuthorizationMiddlewareResultHandler();
}
public async Task HandleAsync(
RequestDelegate requestDelegate,
HttpContext httpContext,
AuthorizationPolicy authorizationPolicy,
PolicyAuthorizationResult policyAuthorizationResult)
{
if (!policyAuthorizationResult.Succeeded)
{
var apiEvent = httpContext.RequestServices.GetService<IApiEventHandler>();
if (apiEvent != null)
{
await apiEvent.OnAuthorizationFailAsync(httpContext, authorizationPolicy, policyAuthorizationResult);
}
httpContext.Response.ContentType = "application/json;charset=utf-8";
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
await httpContext.Response.WriteAsync
(
new ApiResult(ApiResultCode.Unauthorized, SpecificationTip.AuthorizationFail)
{
TrackId = HttpContextHelper.TrackId
}.ToJson(BPASerializer.Json)
);
return;
}
await _handler.HandleAsync(requestDelegate, httpContext, authorizationPolicy, policyAuthorizationResult);
}
}
中间件封装
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseJwtAuthorization(this IApplicationBuilder app)
{
app.UseMiddleware<JwtTokenTransferMiddleware>();
//鉴权(检测是否登录,解析请求登录携带的信息,赋值给HttpContext.User)
app.UseAuthentication();
//授权 (检测权限)
app.UseAuthorization();
return app;
}
}
服务扩展
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddJwtAuthorization(this IServiceCollection services, Action<JwtAuthorizationOptions> setup)
{
services.Configure(setup);
//启用身份验证中间件,身份验证方案(bearer)
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(op =>
{
var options = services.BuildServiceProvider().GetRequiredService<IOptions<JwtAuthorizationOptions>>().Value;
//获取或设置用于验证标识令牌的参数
op.TokenValidationParameters = new TokenValidationParameters
{
//是否验证发行者签发密钥
ValidateIssuerSigningKey = true,
//发行者签发密钥
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SecurityKey)),
//
ValidateIssuer=false,
//发行者
ValidIssuer = options.Issuer,
ValidAudience = options.Audience,
ValidateLifetime = options.ValidateLifetime,
ClockSkew = TimeSpan.FromSeconds(options.ClockSkewSecond)
};
op.Events = new JwtBearerEvents() {
OnMessageReceived = (context) => {
if (!context.HttpContext.Request.Path.HasValue)
{
return Task.CompletedTask;
}
//重点在于这里;判断是Signalr的路径
var accessToken = context.HttpContext.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
Console.WriteLine(path);
Console.WriteLine(accessToken);
if (!(string.IsNullOrWhiteSpace(accessToken)) && path.StartsWithSegments("/chatHub"))
{
context.Token = accessToken;
return Task.CompletedTask;
}
return Task.CompletedTask;
}
};
}
);
//启用权限验证
services.AddAuthorization(options =>
{
options.AddPolicy(BPAPolicyNames.JwtBearer, policy => policy.Requirements.Add(new JwtAuthorizationRequirement()));
});
services.AddTransient<IJwtService, JwtBearerService>();
services.AddTransient<IAuthorizationHandler, JwtAuthorizationHandler>();
services.AddTransient<IAuthorizationMiddlewareResultHandler, AuthorizationResultTransformer>();
// 注入当前用户,替换Thread.CurrentPrincipal的作用
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>()?.HttpContext?.User);
return services;
}
}
打包传送门 https://www.cnblogs.com/inclme/p/16053978.html
.net6中使用
引用上述nuget包
添加扩展服务
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddJwtAuthorization((op) =>
{
builder.Configuration.Bind("BPA:Jwt", op);
//这里配置自定义业务授权处理,比如检查redis token是否存在。。
op.HandleAuthorization = async (o) =>
{
return await Task.FromResult(true);
};
});
var app = builder.Build();
app.UseJwtAuthorization();
使用示例
public class BPAUserIdentity : UserIdentityBase
{
[Claim("test/companyid")]
public string CompanyId { get; set; }
[Claim]
public string RoleId { get; set; }
}
/// <summary>
/// 用户登录
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<JwtInfo> SignInAsync(SignInRequest request)
{
request.Pwd = EncryptHelper.DesEncrypt(request.Pwd, "~1@Aa*<>");
var token = _jwtService.CreateAccessToken(new BPAUserIdentity()
{
Id = IdGenerator.NextId(),
UserName = request.UserName
});
return await Task.FromResult(token);
}
标签:return,Jwt,options,token,new,var,授权,net,public
From: https://www.cnblogs.com/inclme/p/18657666