系统安全功能:系统退出后令牌失效
在现代Web应用中,用户退出系统是一个基本功能,然而,如何确保退出后的令牌不再有效,是系统安全性的一个关键问题。本文将介绍如何通过在用户退出时将JWT令牌加入黑名单,以及在后续请求中验证令牌的有效性,构建系统的双层安全机制,有效应对令牌滥用的风险。
背景
Web应用中的用户认证一般依赖于JWT令牌,它是一种轻量级的身份验证方式。然而,一旦用户退出系统,之前的令牌理论上仍然是有效的,这可能导致一些潜在的安全问题。因此,我们需要一种机制来确保退出后的令牌不再被接受,提升系统的整体安全性。
需求
实现用户安全退出功能。
使退出后的JWT令牌失效,防止滥用。
分析与设计
为了满足上述需求,我们将采用以下方案:
1、将JWT令牌加入黑名单:
在用户退出系统时,获取JWT令牌的ID。
将令牌ID存储到Redis中,构建黑名单。
设置与JWT令牌相同的失效时间,保证黑名单中的令牌会在一定时间后自动清理。
2、验证JWT令牌的有效性:
在每次请求中,获取JWT令牌的Claims。
检查用户对象、Claims对象以及令牌ID是否不为空。
构建黑名单中的key,通过Redis验证令牌是否在黑名单中。
如果在黑名单中,拒绝请求处理。
核心代码
将JWT令牌加入黑名单
// 新增将JWT令牌加入黑名单操作,并设置相同的失效时间
Claims claims = SecureUtil.getClaims(Objects.requireNonNull(request));
if (ObjectUtil.isNotEmpty(claims)) {
Date expiration = claims.getExpiration();
DateTime now = DateTime.now();
long seconds = cn.hutool.core.date.DateUtil.between(now, expiration, DateUnit.SECOND);
String id = claims.getId();
String ExpIdKey = TokenConstant.EXP_ID + ":" + userId + ":" + id;
redisTemplate.opsForValue().set(ExpIdKey, id);
redisTemplate.expire(ExpIdKey, seconds, TimeUnit.SECONDS);
}
验证JWT令牌是否有效
// 拦截器中 校验黑名单
Claims claims = SecureUtil.getClaims(request);
if (user != null && claims != null && StringUtil.isNotEmpty(claims.getId())) {
String key = TokenConstant.EXP_ID + ":" + user.getUserId() + ":" + claims.getId();
String jit = redisTemplate.opsForValue().get(key);
if (claims.getId().equals(jit)) {
log.debug("令牌已失效");
failBack(resp);
return false;
}
}
扩展
通过这种双层安全机制,我们可以进一步扩展系统的安全性:
- 定时任务清理过期令牌: 使用定时任务定期清理黑名单中的过期令牌,确保黑名单不会无限增长。
- 基于角色或权限的失效策略: 根据用户的角色或权限,实现更精细化的令牌失效策略。
- 结合双因素认证: 在令牌验证前加入双因素认证,提升系统的身份验证层级。
总结
通过构建系统的双层安全机制,我们有效地应对了用户退出后令牌的滥用风险。这种机制简单而强大,为Web应用提供了额外的安全保障,使用户退出更具安全性。