文章目錄
- 1. 雙Token 機制概述
- 1.1 訪問令牌(Access Token)
- 1.2 刷新令牌(Refresh Token)
- 2. 雙Token 認證流程
- 3. Spring Boot 具體實現
- 3.1 生成 Token(使用 JWT)
- 3.2 解析 Token
- 3.3 登錄接口(返回雙 Token)
- 3.4 刷新 Token 接口
- 3.5 退出登錄
- 4. 總結 🚀
在微服務架構中,Token 認證是保障系統安全性的重要手段,常見的方式包括 JWT(JSON Web Token) 及 基于 Redis 的 Token 認證。本文將介紹 雙Token 機制 及其具體實現。
1. 雙Token 機制概述
1.1 訪問令牌(Access Token)
- 用途:前端每次請求攜帶該令牌,用于身份認證。
- 有效期:較短(如10分鐘 - 2小時)。
- 存儲方式:前端存儲(如 LocalStorage、SessionStorage、HTTP Only Cookie)。
- 信息載荷:用戶 ID、權限、過期時間等。
1.2 刷新令牌(Refresh Token)
- 用途:用于獲取新的 Access Token,避免用戶頻繁登錄。
- 有效期:較長(如7天 - 30天)。
- 存儲方式:數據庫或 Redis(不能存儲在前端,防止濫用)。
- 信息載荷:用戶 ID,僅用于重新獲取 Access Token。
2. 雙Token 認證流程
-
用戶登錄
- 用戶提交用戶名、密碼。
- 后端校驗通過后,生成 Access Token(短期) 和 Refresh Token(長期)。
- Access Token 通過 JWT 方式返回給前端。
- Refresh Token 存儲到數據庫或 Redis,避免前端篡改。
-
請求 API 資源
- 前端在每次請求時,攜帶
Authorization: Bearer <Access Token>
。 - 后端解析 Access Token,校驗有效性。
- 通過則返回資源,失敗則根據錯誤碼處理(如
401 Unauthorized
)。
- 前端在每次請求時,攜帶
-
Token 過期時的處理
-
Access Token 過期,但 Refresh Token 仍有效 :
- 前端調用 刷新接口,攜帶 Refresh Token 請求新 Access Token。
- 后端驗證 Refresh Token 后,重新生成 Access Token 并返回。
-
Refresh Token 過期或無效:
- 需要用戶重新登錄。
-
-
用戶登出
- 后端刪除 Refresh Token 記錄。
- 前端刪除 Access Token。
3. Spring Boot 具體實現
3.1 生成 Token(使用 JWT)
import io.jsonwebtoken.*;
import java.util.Date;public class JwtUtil {private static final String SECRET_KEY = "your_secret_key";private static final long ACCESS_TOKEN_EXPIRATION = 2 * 60 * 60 * 1000; // 2小時private static final long REFRESH_TOKEN_EXPIRATION = 7 * 24 * 60 * 60 * 1000; // 7天// 生成 Access Tokenpublic static String generateAccessToken(String userId) {return Jwts.builder().setSubject(userId).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION)).signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}// 生成 Refresh Tokenpublic static String generateRefreshToken(String userId) {return Jwts.builder().setSubject(userId).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRATION)).signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}
}
3.2 解析 Token
public static Claims parseToken(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
3.3 登錄接口(返回雙 Token)
@RestController
@RequestMapping("/auth")
public class AuthController {@PostMapping("/login")public Map<String, String> login(@RequestBody LoginRequest request) {// 1. 校驗用戶名和密碼(省略)// 2. 生成 TokenString accessToken = JwtUtil.generateAccessToken(request.getUsername());String refreshToken = JwtUtil.generateRefreshToken(request.getUsername());// 3. 存儲 Refresh Token(示例使用 Redis)redisTemplate.opsForValue().set("refresh_token:" + request.getUsername(), refreshToken, 7, TimeUnit.DAYS);// 4. 返回 TokenMap<String, String> tokens = new HashMap<>();tokens.put("accessToken", accessToken);tokens.put("refreshToken", refreshToken);return tokens;}
}
3.4 刷新 Token 接口
@PostMapping("/refresh")
public ResponseEntity<Map<String, String>> refresh(@RequestHeader("Authorization") String refreshToken) {// 1. 校驗 Refresh TokenClaims claims = JwtUtil.parseToken(refreshToken);String userId = claims.getSubject();// 2. 檢查 Redis 是否存儲該 Refresh TokenString storedToken = redisTemplate.opsForValue().get("refresh_token:" + userId);if (storedToken == null || !storedToken.equals(refreshToken)) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();}// 3. 生成新的 Access TokenString newAccessToken = JwtUtil.generateAccessToken(userId);// 4. 返回新的 TokenMap<String, String> tokens = new HashMap<>();tokens.put("accessToken", newAccessToken);return ResponseEntity.ok(tokens);
}
3.5 退出登錄
@PostMapping("/logout")
public ResponseEntity<Void> logout(@RequestHeader("Authorization") String accessToken) {Claims claims = JwtUtil.parseToken(accessToken);String userId = claims.getSubject();// 刪除 Redis 中的 Refresh TokenredisTemplate.delete("refresh_token:" + userId);return ResponseEntity.ok().build();
}
4. 總結 🚀
機制 | 作用 | 過期時間 | 存儲位置 |
---|---|---|---|
Access Token | 用于 API 認證 | 短(10分鐘-2小時) | 前端 LocalStorage/SessionStorage |
Refresh Token | 用于刷新 Access Token | 長(7天-30天) | Redis/數據庫 |
- 雙 Token 機制 既保證了安全性(短期有效的 Access Token)又提升了用戶體驗(長期有效的 Refresh Token)。
- Access Token 過期時,通過 Refresh Token 重新獲取,而無需重新登錄。
- Refresh Token 需要存儲在后端(如 Redis),避免前端泄露。
博客主頁: 總是學不會.