.net之 Jwt授权

概念

集成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);
        }

posted @   小小青年  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示