8.3JWT提前撤回
当遇到用户被删除、用户在另一个设备上登陆等场景需要将JWT提前撤回,但是JWT是保存在客户端,无法在服务器中进行删除。
解决思路是在用户表中增加一列JWTVersion,用来存储最后一次发放出去的令牌版本号,每次登陆、发放令牌的时候都让JWTVersion自增,当服务器收到客户端提交的JWT后,将客户端的JWTVersion和服务器的进行比较,如果客户端的值小于服务器中的值则过期。
实现步骤:
- 为实体类User增加一个long类型的属性JWTVersion
- 修改产生JWT的代码,实现JWTVersion自增
1 2 3 4 5 6
| user.JWTVersion++; await userManager.UpdateAsync(user); var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); claims.Add(new Claim(ClaimTypes.Name, user.UserName)); claims.Add(new Claim(ClaimTypes.Version, user.JWTVersion.ToString()));
|
- 编写筛选器,统一实现对所有控制器操作方法中JWT的检查工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public class JWTValidationFilter : IAsyncActionFilter { private IMemoryCache memCache; private UserManager<User> userMgr;
public JWTValidationFilter(IMemoryCache memCache, UserManager<User> userMgr) { this.memCache = memCache; this.userMgr = userMgr; }
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var claimUserId = context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier); if (claimUserId == null) { await next(); return; } long userId = long.Parse(claimUserId!.Value); string cacheKey = $"JWTValidationFilter.UserInfo.{userId}"; User user = await memCache.GetOrCreateAsync(cacheKey, async e => { e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5); return await userMgr.FindByIdAsync(userId.ToString()); }); if (user == null) { var result = new ObjectResult($"UserId({userId}) not found"); result.StatusCode = (int)HttpStatusCode.Unauthorized; context.Result = result; return; } var claimVersion = context.HttpContext.User.FindFirst(ClaimTypes.Version); long jwtVerOfReq = long.Parse(claimVersion!.Value); if (jwtVerOfReq >= user.JWTVersion) { await next(); } else { var result = new ObjectResult($"JWTVersion mismatch"); result.StatusCode = (int)HttpStatusCode.Unauthorized; context.Result = result; return; } } }
|
- 注册
1 2 3
| services.Configure<MvcOptions>(opt=>{ opt.Filters.Add<JWTValidationFilter>(); })
|