问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

JWT 续签方案解析与实战

创作时间:
作者:
@小白创作中心

JWT 续签方案解析与实战

引用
CSDN
1.
https://m.blog.csdn.net/zj6182007/article/details/146208546

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 方案原理

  1. 用户登录时,服务器返回 短 Token(Access Token)Refresh Token
  2. Access Token(短 Token) 设定较短的有效期(如 15 分钟),用于用户的身份验证。
  3. Refresh Token 设定较长的有效期(如 7 天或 30 天),用于重新获取新的 Access Token。
  4. 当 Access Token 过期时,前端使用 Refresh Token 请求新的 Access Token。
  5. 如果 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 方案原理

  1. 每次用户请求时,服务器检查 Access Token 的剩余过期时间。
  2. 如果剩余时间 小于一定阈值(如 5 分钟),则生成新 Token 并返回。
  3. 用户活跃时,Token 会不断续期,避免频繁登录。
  4. 如果用户长时间不操作(超过 Token 总时长),则 Token 彻底过期,需要重新登录。

3.2.2 方案优缺点

优点

  • 用户活跃时,Token 续期无感知,提升体验。
  • 适用于 需要频繁交互的应用,如即时通讯、在线编辑等。
  • 只要用户在一定时间内持续使用,Token 不会过期。

缺点

  • 容易导致 Token 被长期滥用,攻击者可持续使用被盗 Token。
  • 服务器压力较大,因为每次请求都可能涉及 Token 续期。

3.2.3 适用场景

  • 适用于 高频访问应用,如 IM、在线文档编辑、直播平台。
  • 适用于 用户行为活跃且需要长时间在线的场景

3.3 方案 3:基于 Redis 存储 Token 状态

3.3.1 方案原理

  1. 服务器在 Redis 存储 Token 及其状态,并设定过期时间。
  2. 每次用户请求时,服务器从 Redis 读取 Token 状态,校验其是否有效。
  3. 续签时,服务器 撤销旧 Token,生成新 Token 并更新 Redis
  4. 如果用户登出或被封禁,服务器可以 主动删除 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;

@Service
public 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 额外功能:黑名单机制

@Service
public 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 状态管理。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号