• .net core webapi jwt 更为清爽的认证 ,续期很简单(1)


    我的方式非主流,控制却可以更加灵活,喜欢的朋友,不妨花一点时间学习一下

    jwt认证分为两部分,第一部分是加密解密,第二部分是灵活的应用于中间件,我的处理方式是将获取token放到api的一个具体的controller中,将发放token与验证分离,token的失效时间,发证者,使用者等信息存放到config中。

    1.配置:

    在appsettings.json中增加配置

    "Jwt": {
    "Issuer": "issuer",//随意定义
    "Audience": "Audience",//随意定义
    "SecretKey": "abc",//随意定义
    "Lifetime": 20, //单位分钟
    "ValidateLifetime": true,//验证过期时间
    "HeadField": "useless", //头字段
    "Prefix": "prefix", //前缀
    "IgnoreUrls": [ "/Auth/GetToken" ]//忽略验证的url
    }

    2:定义配置类:

    internal class JwtConfig
        {
            public string Issuer { get; set; }
            public string Audience { get; set; }
    
            /// <summary>
            /// 加密key
            /// </summary>
            public string SecretKey { get; set; }
            /// <summary>
            /// 生命周期
            /// </summary>
            public int Lifetime { get; set; }
            /// <summary>
            /// 是否验证生命周期
            /// </summary>
            public bool ValidateLifetime { get; set; }
            /// <summary>
            /// 验证头字段
            /// </summary>
            public string HeadField { get; set; }
            /// <summary>
            /// jwt验证前缀
            /// </summary>
            public string Prefix { get; set; }
            /// <summary>
            /// 忽略验证的url
            /// </summary>
            public List<string> IgnoreUrls { get; set; }
        }
    

    3.加密解密接口:

     public interface IJwt
        {
            string GetToken(Dictionary<string, string> Clims);
            bool ValidateToken(string Token,out Dictionary<string ,string> Clims);
        }

    4.加密解密的实现类:

     install-package System.IdentityModel.Tokens.Jwt 

     public class Jwt : IJwt
        {
            private IConfiguration _configuration;
            private string _base64Secret;
            private JwtConfig _jwtConfig = new JwtConfig();
            public Jwt(IConfiguration configration)
            {
                this._configuration = configration;
                configration.GetSection("Jwt").Bind(_jwtConfig);
                GetSecret();
            }
            /// <summary>
            /// 获取到加密串
            /// </summary>
            private void GetSecret()
            {
                var encoding = new System.Text.ASCIIEncoding();
                byte[] keyByte = encoding.GetBytes("salt");
                byte[] messageBytes = encoding.GetBytes(this._jwtConfig.SecretKey);
                using (var hmacsha256 = new HMACSHA256(keyByte))
                {
                    byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                    this._base64Secret= Convert.ToBase64String(hashmessage);
                }
            }
            /// <summary>
            /// 生成Token
            /// </summary>
            /// <param name="Claims"></param>
            /// <returns></returns>
            public string GetToken(Dictionary<string, string> Claims)
            {
                List<Claim> claimsAll = new List<Claim>();
                foreach (var item in Claims)
                {
                    claimsAll.Add(new Claim(item.Key, item.Value??""));
                }
                var symmetricKey = Convert.FromBase64String(this._base64Secret);
                var tokenHandler = new JwtSecurityTokenHandler();
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Issuer = _jwtConfig.Issuer,
                    Audience = _jwtConfig.Audience,
                    Subject = new ClaimsIdentity(claimsAll),
                    NotBefore = DateTime.Now,
                    Expires = DateTime.Now.AddMinutes(this._jwtConfig.Lifetime),
                    SigningCredentials =new SigningCredentials(new SymmetricSecurityKey(symmetricKey),
                                               SecurityAlgorithms.HmacSha256Signature)
                };
                var securityToken = tokenHandler.CreateToken(tokenDescriptor);
                return tokenHandler.WriteToken(securityToken);
            }
            public bool ValidateToken(string Token, out Dictionary<string, string> Clims)
            {
                Clims = new Dictionary<string, string>();
                ClaimsPrincipal principal = null;
                if (string.IsNullOrWhiteSpace(Token))
                {
                    return false;
                }
                var handler = new JwtSecurityTokenHandler();
                try
                {
                    var jwt = handler.ReadJwtToken(Token);
    
                    if (jwt == null)
                    {
                        return false;
                    }
                    var secretBytes = Convert.FromBase64String(this._base64Secret);
                    var validationParameters = new TokenValidationParameters
                    {
                        RequireExpirationTime = true,
                        IssuerSigningKey = new SymmetricSecurityKey(secretBytes),
                        ClockSkew = TimeSpan.Zero,
                        ValidateIssuer = true,//是否验证Issuer
                        ValidateAudience = true,//是否验证Audience
                        ValidateLifetime = this._jwtConfig.ValidateLifetime,//是否验证失效时间
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey
                        ValidAudience = this._jwtConfig.Audience,
                        ValidIssuer = this._jwtConfig.Issuer
                    };
                    SecurityToken securityToken;
                    principal = handler.ValidateToken(Token, validationParameters, out securityToken);
                    foreach (var item in principal.Claims)
                    {
                        Clims.Add(item.Type, item.Value);
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
        }

    5.定义获取Token的Controller:

    在Startup.ConfigureServices中注入 IJwt

     services.AddTransient<IJwt, Jwt>();//Jwt注入 

    [Route("[controller]/[action]")]
        [ApiController]
        public class AuthController : ControllerBase
        {
            private IJwt _jwt;
            public AuthController(IJwt jwt)
            {
                this._jwt = jwt;
            }
            /// <summary>
            /// getToken
            /// </summary>
            /// <returns></returns>
            [HttpPost]
            public IActionResult GetToken()
            {
                if (true)
                {
                    Dictionary<string, string> clims = new Dictionary<string, string>();
                    clims.Add("userName", userName);
                    return new JsonResult(this._jwt.GetToken(clims));
                }
            }
        }

    6.创建中间件:

     public class UseJwtMiddleware
        {
            private readonly RequestDelegate _next;
            private JwtConfig _jwtConfig =new JwtConfig();
            private IJwt _jwt;
            public UseJwtMiddleware(RequestDelegate next, IConfiguration configration,IJwt jwt)
            {
                _next = next;
                this._jwt = jwt;
                configration.GetSection("Jwt").Bind(_jwtConfig);
            }
            public Task InvokeAsync(HttpContext context)
            {
                if (_jwtConfig.IgnoreUrls.Contains(context.Request.Path))
                {
                    return this._next(context);
                }
                else
                {
                    if (context.Request.Headers.TryGetValue(this._jwtConfig.HeadField, out Microsoft.Extensions.Primitives.StringValues authValue))
                    {
                        var authstr = authValue.ToString();
                        if (this._jwtConfig.Prefix.Length > 0)
                        {
                            authstr = authValue.ToString().Substring(this._jwtConfig.Prefix.Length+1, authValue.ToString().Length -(this._jwtConfig.Prefix.Length+1));
                        }
                        if (this._jwt.ValidateToken(authstr, out Dictionary<string, string> Clims))
                        {
                            foreach (var item in Clims)
                            {
                                context.Items.Add(item.Key, item.Value);
                            }
                            return this._next(context);
                        }
                        else
                        {
                            context.Response.StatusCode = 401;
                            context.Response.ContentType = "application/json";
                            return context.Response.WriteAsync("{"status":401,"statusMsg":"auth vaild fail"}");
                        }
                    }
                    else
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        return context.Response.WriteAsync("{"status":401,"statusMsg":"auth vaild fail"}");
                    }
                }
            }
        }

    7.中间件暴露出去

     public static class UseUseJwtMiddlewareExtensions
        {
            /// <summary>
            /// 权限检查
            /// </summary>
            /// <param name="builder"></param>
            /// <returns></returns>
            public static IApplicationBuilder UseJwt(this IApplicationBuilder builder)
            {
                return builder.UseMiddleware<UseJwtMiddleware>();
            }
        }
    

    8.在Startup.Configure中使用中间件:

     app.UseJwt(); 

    以1的配置为例:

    除了请求 /auth/getToken 不需要加头信息外,其他的请求一律要求头信息中必须带着 

    userless:prefix (从Auth/GetToken中获取到的token)

    再提供一个demo的下载链接

    链接: https://pan.baidu.com/s/1tLpZ-HbZJPp37HQVWew8Rg 提取码: 7n9g 

    一些截图: 

    1,在需要认证的的控制器中不需要做任何操作,可以通过httpcontext.item拿到存入clims中的信息

    2.startup截图

     3.发放token

    4.配置

     简单测试

     

    直接请求  无权限

     

    带着token去请求api/values得到响应

    这里的exp就是该token的失效时间(unti时间戳),可以定义一个配置来确定什么时候要去重新生成token,这个动作在中间件中进行(比如给头信息中带上ReToken)客户端下次就用ReToken中的Token重新进行访问,很容易就做到了对token的续期操作

  • 相关阅读:
    【面试题】java基础(一)
    【java集合总结】-- ArrayList源码解析
    【java集合总结】-- 数组总结+自己封装数组类
    【MySQL高可用架构设计】(一)-- mysql复制功能介绍
    【Linux】-- 认识bash shell
    【ORM框架】Spring Data JPA(一)-- 入门
    【数据结构】-- 理解哈希表
    【spring】-- springboot配置全局异常处理器
    【spring】-- jsr303参数校验器
    【web安全】-- springboot实现两次MD5加密
  • 原文地址:https://www.cnblogs.com/zzfstudy/p/10922384.html
Copyright © 2020-2023  润新知