zoukankan      html  css  js  c++  java
  • asp.net core 3.1 自定义中间件实现jwt token认证

    话不多讲,也不知道咋讲!直接上代码

    认证信息承载对象【user】

    /// <summary>
    /// 认证用户信息
    /// </summary>
    public class DyUser
    {
        /// <summary>
        /// 用户ID
        /// </summary>
        public int UserId { get; set; }
        /// <summary>
        /// 所属商户ID
        /// </summary>
        public int? TenantId { get; set; }
    }

    Jwt配置对象

    public class AuthOptions
    {
        /// <summary>
        /// Jwt认证Key
        /// </summary>
        public string Security { get; set; }
        /// <summary>
        /// 过期时间【天】
        /// </summary>
        public int Expiration { get; set; }
    }

    JWT管理接口

    public interface IAuthManage
    {
        /// <summary>
        /// 生成JwtToken
        /// </summary>
        /// <param name="user">用户信息</param>
        /// <returns></returns>
        string GenerateJwtToken(DyUser user);
    }

    JWT管理接口实现

    暂时是使用微软提供类库生成,如果有想法可以自己生成

    public class MicrosoftJwtAuthManage : IAuthManage
    {
        private readonly AuthOptions _authOptions;
        public MicrosoftJwtAuth(AuthOptions authOptions)
        {
            _authOptions = authOptions;
        }
    
        public string GenerateJwtToken(DyUser user)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_authOptions.Security);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim("user",user.ToJson())
                }),
                Expires = DateTime.UtcNow.AddDays(_authOptions.Expiration),//一周过期
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }
    }

    处理JWT中间件

    这里借鉴国外大牛的代码,主要就是验证jwt并且存把解析出来的数据存放到当前上下文

    public class JwtMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly AuthOptions _authOptions;
    
        public JwtMiddleware(RequestDelegate next, AuthOptions authOptions)
        {
            _next = next;
            _authOptions = authOptions;
        }
    
        public async Task Invoke(HttpContext context)
        {
            //获取上传token,可自定义扩展
            var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last()
                        ?? context.Request.Headers["X-Token"].FirstOrDefault()
                        ?? context.Request.Query["Token"].FirstOrDefault()
                        ?? context.Request.Cookies["Token"];
    
            if (token != null)
                AttachUserToContext(context, token);
    
            await _next(context);
        }
    
        private void AttachUserToContext(HttpContext context, string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(_authOptions.Security);
                tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                    ClockSkew = TimeSpan.Zero
                }, out SecurityToken validatedToken);
    
                var jwtToken = (JwtSecurityToken)validatedToken;
                var user = jwtToken.Claims.First(x => x.Type == "user").Value.ToJsonEntity<DyUser>();
    
                //写入认证信息,方便业务类使用
                var claimsIdentity = new ClaimsIdentity(new Claim[] { new Claim("user", jwtToken.Claims.First(x => x.Type == "user").Value) });
                Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity);
    
                // attach user to context on successful jwt validation
                context.Items["User"] = user;
            }
            catch
            {
                // do nothing if jwt validation fails
                // user is not attached to context so request won't have access to secure routes
            }
        }
    }

    权限过滤器

    这个根据刚才中间件的存放的信息判断是否授权成功,支持匿名特性

    public class ApiAuthorizeAttribute : Attribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var user = context.HttpContext.Items["User"];
            //验证是否需要授权和授权信息
            if (HasAllowAnonymous(context) == false && user == null)
            {
                // not logged in
                context.Result = new JsonResult(new {message = "Unauthorized"})
                    {StatusCode = StatusCodes.Status401Unauthorized};
            }
        }
    
        private static bool HasAllowAnonymous(AuthorizationFilterContext context)
        {
            var filters = context.Filters;
            if (filters.OfType<IAllowAnonymousFilter>().Any())
            {
                return true;
            }
    
            // When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
            // were discovered on controllers and actions. To maintain compat with 2.x,
            // we'll check for the presence of IAllowAnonymous in endpoint metadata.
            var endpoint = context.HttpContext.GetEndpoint();
            return endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null;
        }
    }

    扩展IServiceCollection

    方便以后管理和维护,主要就是把需要的对象注入到IOC容器里面

    public static class AuthServiceExtensions
    {
        public static void AddAuth(this IServiceCollection services, Action<AuthOptions> configAction)
        {
            var options = new AuthOptions();
            configAction(options);
            services.AddSingleton(options);
            services.AddSingleton<IAuthManage>(new MicrosoftJwtAuthManage(options));
        }
    }

    NullDySession

    这里是为了在非控制器类获取用户信息用

    /// <summary>
    /// 当前会话对象
    /// </summary>
    public class NullDySession
    {
        /// <summary>
        /// 获取DySession实例
        /// </summary>
        public static NullDySession Instance { get; } = new NullDySession();
        /// <summary>
        /// 获取当前用户信息
        /// </summary>
        public DyUser DyUser
        {
            get
            {
                var claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal;
    
                var claimsIdentity = claimsPrincipal?.Identity as ClaimsIdentity;
    
                var userClaim = claimsIdentity?.Claims.FirstOrDefault(c => c.Type == "user");
                if (userClaim == null || string.IsNullOrEmpty(userClaim.Value))
                {
                    return null;
                }
    
                return userClaim.Value.ToJsonEntity<DyUser>();
            }
        }
    
        private NullDySession()
        {
        }
    }

    到这为止准备工作完成,开始用起来吧~

    修改【Startup.cs->ConfigureServices】

    //添加全局权限认证过滤器
    services.AddControllersWithViews(options =>
    {
        options.Filters.Add<ApiAuthorizeAttribute>();
    })
    //添加认证配置信息
    services.AddAuth(options =>
    {
        options.Expiration = 7;//天为单位
        options.Security = apolloConfig.Get("JwtSecret");
    });

    添加中间件【Startup.cs->Configure(IApplicationBuilder app, IWebHostEnvironment env)方法中】

    注意中间件的位置

    //启用jwt认证中间件
    app.UseMiddleware<JwtMiddleware>();

    api使用案例【使用构造注入IAuthManage】

    //生成了JwtToken
    var token = _authManage.GenerateJwtToken(new DyUser {UserId = userId, TenantId = tenantId});
    
    //Controller里面获取用户信息
    public DyUser DyUser => (DyUser)this.HttpContext.Items["User"];
    
    //普通class类获取用户信息【如果不是Web应用,需要独立引用Dymg.Core】
    NullDySession.Instance.DyUser.UserId;
    
    //如果个别不接口不需要认证,可以使用AllowAnonymous特性
    [HttpPost, AllowAnonymous]
    public string Noauth()
    {
        return "这个不需要授权";
    }

    前端调用案例

    //token放在请求头里面
    Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y
    //自定义key
    x-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y
    
    //使用连接字符串方式
    https://xxxxx/user/getUser?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y

    转:https://www.cnblogs.com/jackgmchen/p/13416069.html

  • 相关阅读:
    梦断代码,读书计划
    《梦断代码》读后感1
    四则运算2详细过程
    四则运算2初步构思
    四则运算
    阅读《梦断代码》计划
    四则运算2设计思路
    随机输出30道四则运算题
    软件工程阅读计划
    面试题04
  • 原文地址:https://www.cnblogs.com/superfeeling/p/14990064.html
Copyright © 2011-2022 走看看