前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.net core webapi jwt 更为清爽的认证 ,续期很简单(2)

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

作者头像
FreeTimeWorker
发布2020-08-31 15:21:07
8150
发布2020-08-31 15:21:07
举报
文章被收录于专栏:C#开发点点滴滴C#开发点点滴滴

.net core webapi jwt 更为清爽的认证 后续:续期以及设置Token过期

续期: 续期的操作是在中间件中进行的,续期本身包括了前一个Token的过期加发放新的Token,所以在说续期前先说Token过期

在开始之前先增加相应的配置:续期间隔 和 续期携带给前端的新Token的Head.jwtConfig同步修改

代码语言:javascript
复制
 "Jwt": {
    "Issuer": "issuer",
    "Audience": "Audience",
    "SecretKey": "abc",
    "Lifetime": 20, //单位分钟
    "RenewalTime": 10, //单位分钟,Token续期的时间间隔,10表示超过10分钟再次请求就续期
    "ValidateLifetime": true,
    "HeadField": "Auth", //头字段
    "ReTokenHeadField": "ReToken",
    "Prefix": "", //前缀
    "IgnoreUrls": [ "/swagger/index.html", "/swagger/v1/swagger.json", "/Auth/GetToken", "/Auth/InvalidateToken" ]
  }
代码语言:javascript
复制
 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 int RenewalTime { get; set; }
        /// <summary>
        /// 是否验证生命周期
        /// </summary>
        public bool ValidateLifetime { get; set; }
        /// <summary>
        /// 验证头字段
        /// </summary>
        public string HeadField { get; set; }
        /// <summary>
        /// 新Token的Head字段
        /// </summary>
        public string ReTokenHeadField { get; set; }
        /// <summary>
        /// jwt验证前缀
        /// </summary>
        public string Prefix { get; set; }
        /// <summary>
        /// 忽略验证的url
        /// </summary>
        public List<string> IgnoreUrls { get; set; }
    }

1.设置Token过期

首先在Jwt.cs中增加静态属性

代码语言:javascript
复制
public static List<string> InvalidateTokens = new List<string>();

然后添加 Jwt中添加方法:

代码语言:javascript
复制
bool InvalidateToken(string Token);
代码语言:javascript
复制
public bool InvalidateToken(string Token)
        {
            if (!InvalidateTokens.Contains(Token))
            {
                InvalidateTokens.Add(Token);
            }
            return true;
        }

修改Jwt中GetToken的方法:

代码语言:javascript
复制
string GetToken(IDictionary<string, string> Clims,string OldToken=null);
代码语言:javascript
复制
  public string GetToken(IDictionary<string, string> Claims,string OldToken=null)
        {
            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);
            if (!string.IsNullOrEmpty(OldToken))//执行旧Token过期
            {
                if (!InvalidateTokens.Contains(OldToken))
                {
                    InvalidateTokens.Add(OldToken);
                }
            }
            return tokenHandler.WriteToken(securityToken);
        }

修改: ValidateToken

代码语言:javascript
复制
  public bool ValidateToken(string Token, out Dictionary<string, string> Clims)
        {
            Clims = new Dictionary<string, string>();
            if (InvalidateTokens.Contains(Token))
            {
                return false;
            }
            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)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }
        }

紧接着在Auth中增加接口

代码语言:javascript
复制
/// <summary>
        /// 强制Token失效
        /// </summary>
        /// <param name="Token"></param>
        /// <returns></returns>
        [HttpPost]
        public IActionResult InvalidateToken(string Token)
        {
            return new JsonResult(this._jwt.InvalidateToken(Token));
        }

//需要让当前Token强制过期的时候,客户端调用 InvalidateToken 传入当前Token就可以

2.续期:修改中间件:UseJwtMiddleware

代码语言:javascript
复制
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)&&!Jwt.InvalidateTokens.Contains(authstr))
                    {
                        List<string> climsKeys = new List<string>() { "nbf", "exp", "iat", "iss","aud" };
                        IDictionary<string, string> RenewalDic = new Dictionary<string, string>();
                        foreach (var item in Clims)
                        {
                            if (climsKeys.FirstOrDefault(o=>o==item.Key) == null)
                            {
                                context.Items.Add(item.Key, item.Value);
                                RenewalDic.Add(item.Key, item.Value);
                            }
                        }
                        //验证通过的情况下判断续期时间
                        if (Clims.Keys.FirstOrDefault(o => o == "exp") != null)
                        {
                            var start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                            var timespan = long.Parse(Clims["exp"]);
                            var expDate = start.AddSeconds(timespan).ToLocalTime();
                            var o = expDate - DateTime.Now;
                            if (o.TotalMinutes < _jwtConfig.RenewalTime)
                            {
                                //执行续期当前Token立马失效
                                //var newToken = this._jwt.GetToken(RenewalDic, authstr);
                   //var newToken=this._jwt.GetToken(RenewalDic);//生成新Token当前Token仍可用,过期时间以Lifetime设置为准
                                context.Response.Headers.Add(_jwtConfig.ReTokenHeadField, newToken);
                            }
                        }
                        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\"}");
                }
            }
        }
    }

本例中,当客户端获取Token超过10分钟未超过20分钟的这个时间段如果再执行请求,那么服务端就会给Head头上带上 ReToken:newToken

下次请求带着新Token过来就可以

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档