1. 引言
JWT(JSON Web Token)是一种常用的身份验证方式,它具有无状态、易扩展等特点,广泛应用于前后端分离项目。然而,JWT 采用固定过期时间,导致在 Token 过期后用户需要重新登录,影响用户体验。因此,我们需要一种 JWT 续签方案,保证安全性的同时提高用户体验。

2. JWT 基本概念
2.1 JWT 组成
JWT 由三部分组成:
-
Header(头部):存储 Token 类型和签名算法。
-
Payload(载荷):包含用户信息和声明(Claims)。
-
Signature(签名):用于验证 Token 是否被篡改。
示例 JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2.2 JWT 过期机制
JWT 在签发时通常包含 exp(过期时间)字段,例如:
{"sub": "user123","exp": 1710022400}
一旦 exp 时间到达,Token 就会失效。

3. JWT 续签方案(详细解析)
在 JWT 认证体系中,Token 的续签涉及安全性、性能和用户体验的平衡。以下是三种常见的续签方案的详细说明,并探讨其优缺点及适用场景。
3.1 方案 1:短 Token + 刷新 Token 机制(常见方案)
3.1.1 方案原理
-
用户登录时,服务器返回 短 Token(Access Token) 和 Refresh Token。
-
Access Token(短 Token) 设定较短的有效期(如 15 分钟),用于用户的身份验证。
-
Refresh Token 设定较长的有效期(如 7 天或 30 天),用于重新获取新的 Access Token。
-
当 Access Token 过期时,前端使用 Refresh Token 请求新的 Access Token。
-
如果 Refresh Token 也过期,则需要用户重新登录。
3.1.2 方案优缺点
✅ 优点:
-
Access Token 失效后,即使被盗,攻击者的可用时间有限。
-
适用于大多数 Web 应用,兼顾安全性与用户体验。
-
Refresh Token 由服务器控制,可用于强制用户登出。
❌ 缺点:
-
需要额外存储 Refresh Token(一般存储在 HttpOnly Cookie 或 数据库)。
-
需要额外的接口处理 Refresh Token 续签。
-
Refresh Token 仍有一定风险,若被盗仍可换取新的 Access Token。
3.1.3 适用场景
-
适用于 大多数 Web 和移动端应用,如企业管理系统、社交应用等。
-
适用于 希望减少用户频繁登录的场景,但对安全性要求较高。
3.2 方案 2:滑动过期(Sliding Expiration)
3.2.1 方案原理
-
每次用户请求时,服务器检查 Access Token 的剩余过期时间。
-
如果剩余时间 小于一定阈值(如 5 分钟),则生成新 Token 并返回。
-
用户活跃时,Token 会不断续期,避免频繁登录。
-
如果用户长时间不操作(超过 Token 总时长),则 Token 彻底过期,需要重新登录。
3.2.2 方案优缺点
✅ 优点:
-
用户活跃时,Token 续期无感知,提升体验。
-
适用于 需要频繁交互的应用,如即时通讯、在线编辑等。
-
只要用户在一定时间内持续使用,Token 不会过期。
❌ 缺点:
-
容易导致 Token 被长期滥用,攻击者可持续使用被盗 Token。
-
服务器压力较大,因为每次请求都可能涉及 Token 续期。
3.2.3 适用场景
-
适用于 高频访问应用,如 IM、在线文档编辑、直播平台。
-
适用于 用户行为活跃且需要长时间在线的场景。
3.3 方案 3:基于 Redis 存储 Token 状态
3.3.1 方案原理
-
服务器在 Redis 存储 Token 及其状态,并设定过期时间。
-
每次用户请求时,服务器从 Redis 读取 Token 状态,校验其是否有效。
-
续签时,服务器 撤销旧 Token,生成新 Token 并更新 Redis。
-
如果用户登出或被封禁,服务器可以 主动删除 Redis 记录,令 Token 失效。
3.3.2 方案优缺点
✅ 优点:
-
支持服务器主动让 Token 失效,避免 Token 被长期滥用。
-
提升安全性,即使 Token 被盗,也可立即废止。
-
适用于需要用户管理和权限控制的场景。
❌ 缺点:
-
增加了 Redis 存储和查询成本,影响系统性能。
-
需要额外维护 Token 状态,增加复杂度。
3.3.3 适用场景
-
安全要求较高的应用,如 金融系统、电商后台管理系统。
-
需要主动撤销 Token 的场景,如管理员强制登出用户。
-
适用于分布式架构,Redis 可以共享 Token 状态。

4. 实战代码:JWT 续签实现(Spring Boot)
4.1 方案 1:短 Token + 刷新 Token 实现
@RestController@RequestMapping("/auth")public class AuthController {@PostMapping("/refresh")public ResponseEntity<?> refreshToken(@RequestParam String refreshToken) {try {Claims claims = Jwts.parser().setSigningKey("mySecretKey").parseClaimsJws(refreshToken).getBody();String newToken = JwtUtil.generateToken(claims.getSubject(), false);return ResponseEntity.ok(newToken);} catch (ExpiredJwtException e) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Refresh Token 过期");}}}
4.2 方案 2:滑动过期实现
import io.jsonwebtoken.*;import java.util.Date;public class JwtUtil {private static final String SECRET_KEY = "mySecretKey";private static final long EXPIRATION_TIME = 15 * 60 * 1000; // 15分钟private static final long SLIDING_WINDOW = 5 * 60 * 1000; // 5分钟public static String generateToken(String userId) {return Jwts.builder().setSubject(userId).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)).signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}public static boolean shouldRenewToken(Date expiration) {return (expiration.getTime() - System.currentTimeMillis()) < SLIDING_WINDOW;}}
@RestController@RequestMapping("/secure")public class SecureController {@GetMapping("/data")public ResponseEntity<?> getData(@RequestHeader("Authorization") String token) {try {Claims claims = Jwts.parser().setSigningKey("mySecretKey").parseClaimsJws(token.replace("Bearer ", "")).getBody();if (JwtUtil.shouldRenewToken(claims.getExpiration())) {String newToken = JwtUtil.generateToken(claims.getSubject());return ResponseEntity.ok().header("Authorization", "Bearer " + newToken).body("新Token已生成");}return ResponseEntity.ok("请求成功,无需续签");} catch (ExpiredJwtException e) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Token 过期");}}}
4.3 方案 3:基于 Redis 存储 Token 状态实现
import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Servicepublic class RedisTokenService {private final StringRedisTemplate redisTemplate;private static final long TOKEN_EXPIRATION = 15 * 60;public RedisTokenService(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}public void storeToken(String userId, String token) {redisTemplate.opsForValue().set("TOKEN:" + userId, token, TOKEN_EXPIRATION, TimeUnit.SECONDS);}public boolean isTokenValid(String userId, String token) {String storedToken = redisTemplate.opsForValue().get("TOKEN:" + userId);return token.equals(storedToken);}}
4.4 额外功能:黑名单机制
@Servicepublic class TokenBlacklistService {private final StringRedisTemplate redisTemplate;public TokenBlacklistService(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}public void addToBlacklist(String token) {redisTemplate.opsForValue().set("BLACKLIST:" + token, "true", 1, TimeUnit.HOURS);}public boolean isBlacklisted(String token) {return redisTemplate.hasKey("BLACKLIST:" + token);}}

5. 选择合适的 JWT 续签方案
|
方案 |
适用场景 |
安全性 |
复杂度 |
|---|---|---|---|
|
短 Token + Refresh Token |
适用于大多数 Web 应用 |
高 |
中 |
|
滑动过期 |
适用于高频访问应用 |
低 |
低 |
|
Redis 方案 |
适用于安全要求高的应用 |
最高 |
高 |
6. 总结
JWT 续签方案的选择取决于 安全性 和 用户体验。短 Token + Refresh Token 方案是目前最常见的方式,兼顾安全性和可用性。如果需要更高的控制能力,可以结合 Redis 进行 Token 状态管理。
如果这篇文章对你有所帮助,欢迎 点赞、收藏、转发!🎯
**
如需Java面试题资料,可关注公众号:小健学Java,回复“面试”即可获得!
**



2万+

被折叠的 条评论
为什么被折叠?



