Live2D 看板娘 / Demo

jwt 滑动过期方案

  网上大多实现jwt滑动过期的方案要结合Redis,或者返回俩个token,我这里介绍一个稍微简单点的方案,由于 jwt的总的有效时间是 expires 加上 ClockSkew,

那么我们就在这个ClockSkwe(滑动过期时间)上做做文章

我们首先创建个配置类

 public class JwtKeyConfig
    {
        /// <summary>
        /// 秘钥
        /// </summary>
        public string SecurityKey { get; set; }

        /// <summary>
        /// 签发人
        /// </summary>
        public string Issuer { get; set; }

        /// <summary>
        /// 接收人
        /// </summary>
        public string Audience { get; set; }

        /// <summary>
        /// 过期时间  分钟
        /// </summary>
        public int Expire { get; set; }

        /// <summary>
        /// 缓冲过期时间 分钟
        /// </summary>
        public int ClockSkew { get; set; }
    }

  //添加jwt验证
            services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.Challenge = JwtBearerDefaults.AuthenticationScheme;
                    options.RequireHttpsMetadata = false;

                    if (!string.IsNullOrEmpty(jwtKeyConfig.Audience))
                        options.Audience = jwtKeyConfig.Audience;

                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = jwtKeyConfig.Issuer,
                        //缓冲过期时间,总的有效时间等于这个时间加上过期时间
                        ClockSkew = TimeSpan.FromMinutes(jwtKeyConfig.ClockSkew),
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKeyConfig.SecurityKey))
                    };
                    if (null != func)
                        options.Events = new JwtBearerEvents
                        {
                            OnMessageReceived = (context) => { return func.Invoke(context); }
                        };
                });

生成Toekn

  private string CreateToken(Claim[] claims)
        {
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtKeyConfig.SecurityKey));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(
                _jwtKeyConfig.Issuer,
                _jwtKeyConfig.Audience,
                claims,
          //重点是这里 expires: DateTime.Now.AddMinutes(_jwtKeyConfig.Expire), signingCredentials: credentials);
return $"Bearer {new JwtSecurityTokenHandler().WriteToken(token)}"; }

这样,假如我们的配置类似这样

{
    "SecurityKey": "123",
    "Issuer": "123",
    "Audience": "123",
    "Expire": 120,
    "ClockSkew": 30
}

那么其实生成的Token总的有效时间是 150分钟,也是是2个半小时, 那么我们在前端俩个小时以后再次请求Token的时候 , 判断一下,然后给用户在返回头中重新生成一个Toekn,前端获取到返回头中有对应的key值,获取到新的token,更新他们本地的token。

判断方法我们写到过滤器中

  public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {
            if (context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authHeader))
            {
                var token = authHeader.ToString().Split(' ')[1];
                var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
                var jwtPayload = jwtToken.Payload; //获取payload中的数据 

                if (jwtPayload.Exp != null)
                {
                    var expTime = jwtPayload.Exp.Value;
                    var now = DateTime.Now.ToTimestamp();
                    //如果Token已过期,重新生成Token放在返回头中返回
                    //Token过期时间为设置的过期时间加缓冲过期时间,相当于在缓冲过期时间段内进行请求,将返回一个新的Token
                    if (now > expTime)
                    {
                        var claims = jwtPayload.Claims as List<Claim>;
                        var userId = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
                        var userName = claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value ?? string.Empty;
                        var refreshToken = await _tokenService.CreateTokenAsync(userId, userName);
                        context.HttpContext.Response.Headers.Add("Set-Authorization", refreshToken);
                    }
                }
            }

            await next();
        }

 

posted @ 2022-10-17 10:12  MChuang  阅读(600)  评论(0编辑  收藏  举报