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(); }
喜欢的朋友请帮忙点个赞!!!