JWT(JSON Web Tokens) 详解
8

一、JWT 是什么?

JWT(JSON Web Token)​​ 是一种基于 JSON 的开放标准(RFC 7519),用于在各方之间​​安全传输信息​​。它由三部分组成,用点号 . 分隔:

​Header(头部)​​:声明令牌类型(如 JWT)和签名算法(如 HS256/RS256

  • 示例:

    {
      "alg": "HS256",
      "typ": "JWT"
    }
  • ​Base64Url编码后​​:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

​Payload(负载)​​:包含声明信息(Claims),如用户身份(sub)、过期时间(exp)、权限等。数据以 Base64 编码,​​非加密​​,敏感信息需额外加密

  • 示例:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true,
      "iat": 1516239022
    }
  • ​Base64Url编码后​​:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0

Signature(签名)​​:对 Header 和 Payload 用密钥签名,确保数据完整性和防篡改

  • 示例(使用密钥 your-256-bit-secret):

    HMACSHA256(
      base64UrlEncode(header) + "." + base64UrlEncode(payload),
      your-256-bit-secret
    )
  • ​签名结果(Base64Url编码后)​​:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

完整JWT示例

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

完整jJWT = base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + 对前面那两个base64编码的字符串 用"."组合在一起 再用密钥加密一次

如果需要生成或调试JWT,可以使用在线工具: jwt.io

二、怎么用?工作流程

  1. ​用户登录​​:客户端发送凭据(如用户名/密码)到服务器

  2. ​生成 JWT​​:服务器验证凭据后,生成 JWT 并返回给客户端

  3. ​存储 JWT​​:客户端将 JWT 存储在 localStoragesessionStorage 或 Cookie 中

  4. ​发送 JWT​​:客户端在后续请求的 Authorization 头中携带 JWT(如 Bearer <token>

  5. ​验证 JWT​​:服务器验证签名、有效期及声明,确认请求合法

三、什么时候用它?

适用场景

  1. ​用户认证与授权​​:

    • 登录后生成 JWT 作为身份凭证,用于后续 API 请求的权限验证。

  2. ​单点登录(SSO)​​:

    • 用户登录一次,JWT 在多个关联系统间共享身份状态。

  3. ​API 安全​​:

    • 微服务间通过 JWT 传递用户身份和权限,替代 Session 会话。

  4. ​无状态系统​​:

    • 需横向扩展的分布式架构(如云原生应用)。

  5. ​临时访问​​:

    • 密码重置链接、短期资源访问令牌。

四、用户登录了,如果中途过期了怎么办?

当用户登录后,如果 JWT 过期,可以通过 ​​双 Token 机制(Access Token + Refresh Token)​​,生成双Token

// 生成双 Token
String accessToken = JwtUtil.generateAccessToken(request.getUsername());    // 推荐设置15-30 分钟
String refreshToken = JwtUtil.generateRefreshToken(request.getUsername());  // 推荐设置7天

创建拦截器,在请求前检查 Access Token 是否过期,若过期则用 Refresh Token 获取新 Access Token,来替换原来的Access Token,从而实现无感知刷新。 如果Refresh Token 过期了呢?当然是跳转登录界面重新登录了。

五、用户主动登出时,如何立即失效让Token失效呢​?

当用户主动登出时,需要确保所有相关的令牌(Token)立即失效,即使它们尚未过期。

因为JWT的特性是无状态的,服务端无法直接修改或删除已签发的 Token。前端直接清理所有token,后端可以折中的在Redis引入一个黑名单,使它失效,虽然使用 Redis 存储 Token 黑名单确实会破坏 JWT 的“无状态”设计原则​​,但这是一种必要的权衡。

1. ​​ 立即失效 Access Token​​

  • ​解决方案​​:

    • ​短有效期​​:Access Token 本身已设为短期(如 30 分钟),登出后等待其自然过期。

    • ​客户端删除​​:前端清除存储的 Access Token(localStorage 或内存)。

2. 强制失效 Refresh Token​​

  • ​关键步骤​​(必须服务端参与):

    1. ​服务端维护 Refresh Token 黑名单​​。

    2. 用户登出时,将 Refresh Token 标记为失效(如存入数据库或 Redis)。

    3. 后续刷新请求时,检查 Refresh Token 是否已被撤销。

​​3. 清除客户端存储​

  • 前端删除所有 Token 和会话相关的存储:

// 前端登出逻辑
function logout() {
  localStorage.removeItem('access_token');
  localStorage.removeItem('refresh_token');
  // 如果是 Cookie,设置过期时间为过去
  document.cookie = 'refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
  // 跳转登录页
  window.location.href = '/login';
}
  • 服务端实现方案

使用Refresh Token 黑名单(Redis + JWT)

@PostMapping("/logout")
public ResponseEntity<?> logout(
    @RequestHeader("Authorization") String accessToken,
    @CookieValue(value = "refresh_token", required = false) String refreshToken
) {
    // 1. 解析 Access Token 获取用户名(无需验证过期)
    String username = JwtUtil.getUsernameFromToken(accessToken.replace("Bearer ", ""));
    
    // 2. 将 Refresh Token 加入黑名单(存储哈希值)
    String refreshTokenHash = hash(refreshToken);
    redisTemplate.opsForValue().set(
        "blacklist:" + refreshTokenHash, 
        username, 
        JwtUtil.getRemainingExpiration(refreshToken) // 剩余有效期
    );
    
    // 3. 清除客户端 Cookie(可选)
    ResponseCookie cookie = ResponseCookie.from("refresh_token", "")
        .maxAge(0)
        .path("/")
        .httpOnly(true)
        .secure(true)
        .build();
    
    return ResponseEntity.ok()
        .header(HttpHeaders.SET_COOKIE, cookie.toString())
        .body("登出成功");
}

验证 Refresh Token 时检查黑名单​​:

public boolean isRefreshTokenValid(String refreshToken) {
    // 检查签名和过期时间
    if (!JwtUtil.validateToken(refreshToken)) return false;
    
    // 检查是否在黑名单中
    String refreshTokenHash = hash(refreshToken);
    return !redisTemplate.hasKey("blacklist:" + refreshTokenHash);
}

总结​​

JWT 是一种用签名保护的 JSON 数据令牌,适合无状态认证,但需注意安全设计(密钥、过期时间、敏感数据)。

JWT(JSON Web Tokens) 详解
https://www.orioncoder.cn/archives/m1Xgn0eG
作者
Orion
发布于
更新于
许可