You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot后端Refresh Token选型:UUID还是JWT?及相关疑问

关于Refresh Token:UUID vs JWT的选型及相关问题解答

一、UUID vs JWT作为Refresh Token的选型分析

UUID方案特点

  • 优点:
    • Token为完全随机字符串,无任何明文信息泄露风险;
    • 所有Token状态存储在数据库,支持主动失效(如用户登出、修改密码时直接删除对应记录);
    • 实现逻辑简单,依赖数据库校验合法性。
  • 缺点:
    • 每次校验Refresh Token都需查询数据库,高并发场景下会增加数据库压力;
    • 需要维护refresh_token表,增加存储成本。

JWT方案特点

  • 优点:
    • 无需查询数据库即可校验过期时间,性能更优;
    • 载荷可存储少量非敏感信息(如用户ID),减少额外业务查询;
    • 无状态设计,适配分布式系统架构。
  • 缺点:
    • 一旦签发无法主动失效,除非引入黑名单机制(但会增加存储依赖);
    • 签名密钥泄露后,攻击者可伪造合法Refresh Token;
    • 载荷采用Base64编码(非加密),不能存储敏感数据。

选型建议

没有绝对最优方案,需结合业务场景判断:

  • 若系统对主动失效需求高(如频繁的用户登出、密码修改操作),或系统规模较小,UUID方案更安全可控;
  • 若系统为分布式架构,追求无状态、高并发性能,且可接受Refresh Token无法主动失效(或通过缩短有效期+黑名单机制弥补),则JWT更合适。但必须严格保证签名密钥的安全性,且载荷中禁止存放敏感信息。

二、JWT类型Refresh Token是否需要哈希存储?

必须哈希存储!即使JWT带有签名,存储时也应使用哈希算法(如BCrypt、SHA-256加盐)处理,原因如下:

  • 避免数据库泄露后,攻击者直接获取到可直接使用的有效Refresh Token(JWT本身可解码,泄露后能被直接利用);
  • 哈希后的Token无法逆向还原,即使数据泄露,攻击者也无法直接使用;
  • 哈希时务必加盐,防止彩虹表攻击。

三、额外问题:使用org.springframework.security.oauth2.jwt时,控制器中如何获取过期声明?

场景1:Refresh Token作为Bearer Token在请求头中

Spring Security会自动解析请求头中的JWT,可直接在控制器方法参数中注入Jwt对象获取过期声明:

import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RefreshTokenController {

    @GetMapping("/check-refresh-expiry")
    public String getRefreshTokenExpiry(Jwt jwt) {
        // 获取过期时间的Instant对象
        Instant expiryTime = jwt.getExpiresAt();
        // 或直接获取"exp"声明的时间戳(Long类型)
        Long expiryTimestamp = jwt.getClaim("exp");
        return "Refresh Token过期时间:" + expiryTime;
    }
}

场景2:Refresh Token放在请求参数/自定义头中

可通过JwtDecoder手动解析Token并获取过期声明:

import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RefreshTokenController {

    private final JwtDecoder jwtDecoder;

    // 构造注入JwtDecoder
    public RefreshTokenController(JwtDecoder jwtDecoder) {
        this.jwtDecoder = jwtDecoder;
    }

    @GetMapping("/verify-refresh-token")
    public String verifyRefreshToken(@RequestParam String refreshToken) {
        Jwt jwt = jwtDecoder.decode(refreshToken);
        Instant expiryTime = jwt.getExpiresAt();
        return "Refresh Token过期时间:" + expiryTime;
    }
}

内容的提问来源于stack exchange,提问作者Johnny boy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 15:40:29