双 Token 认证机制详解——原理、实现及代码示例

在现代 Web 开发中,Token 认证 是最常见的身份验证方式之一。相比于传统的 Session 认证,Token 认证具有更高的安全性和可扩展性。而 双 Token 认证(Access Token + Refresh Token) 进一步解决了 Token 认证可能面临的安全问题,例如 Token 过期、频繁重新登录等。

本篇博客将详细介绍双 Token 认证的概念、工作流程、代码实现,并分析其优缺点。


一、为什么需要双 Token 认证?

单 Token 认证(通常是 JWT)存在几个主要问题:

  1. Token 过期后需要重新登录

    • 如果 Token 过期,用户需要重新输入用户名和密码登录,影响用户体验。
  2. 长期有效的 Token 安全性低

    • 为了减少用户频繁登录,可以设定一个长期有效的 Token,但如果 Token 被盗,将导致安全隐患。
  3. Token 不能轻易撤销

    • 由于 JWT 是无状态的,一旦生成无法在服务器端主动失效,导致无法撤销已泄露的 Token。

双 Token 认证 通过 Access Token + Refresh Token 解决了这些问题:

  • Access Token(短时令牌):
    • 用于访问受保护的 API,生命周期较短(如 15~30 分钟)。
  • Refresh Token(刷新令牌):
    • 用于获取新的 Access Token,生命周期较长(如 7 天或 30 天)。
    • 仅在服务器端存储并验证,不能用于访问 API。

二、双 Token 认证的工作流程

1. 用户登录并获取 Token

用户使用 用户名 + 密码 进行身份验证,服务器返回:

  • 一个短期有效的 Access Token(用于访问 API)。
  • 一个长期有效的 Refresh Token(用于获取新的 Access Token)。
客户端请求:
POST /api/auth/login
{
  "username": "user1",
  "password": "password123"
}

服务器返回:
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsIn...",
  "refreshToken": "g1a2b3c4d5e6f7g8h9i0"
}

2. 访问受保护 API

  • 客户端每次访问受保护的 API 时,都会携带 Access Token 作为 Authorization 头部。
  • 服务器验证 Token 是否有效,如果有效,则返回数据,否则返回 401 未授权。

3. Access Token 过期,使用 Refresh Token 重新获取

  • 如果 Access Token 过期,客户端使用 Refresh Token 请求新的 Access Token。
  • 服务器验证 Refresh Token 的有效性:
    • 有效:返回新的 Access Token,并可以选择返回新的 Refresh Token。
    • 无效(如 Refresh Token 过期):需要重新登录。
POST /api/auth/refresh
{
  "refreshToken": "g1a2b3c4d5e6f7g8h9i0"
}

服务器返回:
{
  "accessToken": "new-access-token...",
  "refreshToken": "new-refresh-token..."
}

三、代码实现(Spring Boot + JWT)

1. JWT工具类

import io.jsonwebtoken.*;
import java.util.Date;

public class JwtUtil {
    private static final String SECRET_KEY = "my-secret-key";
    private static final long ACCESS_TOKEN_EXPIRATION = 15 * 60 * 1000; // 15分钟
    private static final long REFRESH_TOKEN_EXPIRATION = 7 * 24 * 60 * 60 * 1000; // 7天

    // 生成 Access Token
    public static String generateAccessToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    // 生成 Refresh Token
    public static String generateRefreshToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    // 解析 Token
    public static Claims parseToken(String token) throws ExpiredJwtException {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
    }
}

2.用户登录,返回token

@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
    if (isValidUser(request.getUsername(), request.getPassword())) {
        String accessToken = JwtUtil.generateAccessToken(request.getUsername());
        String refreshToken = JwtUtil.generateRefreshToken(request.getUsername());

        return ResponseEntity.ok(new AuthResponse(accessToken, refreshToken));
    }
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}

4.刷新token

@PostMapping("/refresh")
public ResponseEntity<?> refresh(@RequestBody RefreshRequest request) {
    try {
        Claims claims = JwtUtil.parseToken(request.getRefreshToken());
        String newAccessToken = JwtUtil.generateAccessToken(claims.getSubject());
        return ResponseEntity.ok(new AuthResponse(newAccessToken, request.getRefreshToken()));
    } catch (ExpiredJwtException e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Refresh Token 已过期");
    }
}

四、双 Token 认证的优势

  1. 安全性更高

    • Access Token 过期较快,即使被盗,影响范围有限。
    • Refresh Token 仅存储在服务器端,不能直接访问 API,提高安全性。
  2. 提升用户体验

    • Access Token 过期后,用户无需重新登录,只需使用 Refresh Token 自动刷新。
  3. 更灵活的 Token 管理

    • 服务器可以在 Refresh Token 失效时让用户重新登录,以提高安全性。

五、总结

  • Access Token 适用于短期访问,过期后需要使用 Refresh Token 获取新 Token。
  • Refresh Token 只应存储在服务器端,避免暴露给前端。
  • 双 Token 认证 提供更高的安全性,同时减少用户的重复登录,提高体验。

这种方式广泛应用于现代 Web 开发,尤其是 SPA(单页应用)、移动应用和微服务架构中。如果你的系统需要更高的安全性,建议结合 Redis 或数据库存储 Refresh Token 以便进行主动撤销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值