一、JWT 的簡單了解
1. 什么是 JWT?
JWT(JSON Web Token)是一種開放標準(RFC 7519),用于在 各方之間安全地傳輸信息。它基于 JSON 格式,信息通過 數字簽名 的方式保證不可篡改,常用于 身份認證和信息交換。
2. JWT 的結構
JWT 由三部分組成,每部分用
.
分隔:Header.Payload.Signature
(1) Header(頭部)
描述 類型 和 簽名算法。例如:
{"alg": "HS256","typ": "JWT" }
Base64Url 編碼后得到第一部分。
(2) Payload(載荷)
存放 聲明(Claims) 的地方。
聲明分三類:
注冊聲明(Registered Claims):建議但非必須使用的標準字段
iss
:簽發者 (issuer)
sub
:主題 (subject)
aud
:接收者 (audience)
exp
:過期時間 (expiration time)
nbf
:生效時間 (not before)
iat
:簽發時間 (issued at)
jti
:JWT ID,用于防止重放攻擊公共聲明(Public Claims):大家約定好的字段
私有聲明(Private Claims):系統內部定義的字段,比如
userId
、role
。例如:
{"sub": "user123","name": "Alice","role": "admin","exp": 1692345600 }
Base64Url 編碼后得到第二部分。
(3) Signature(簽名)
用于防篡改。
計算方法:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret )
即:把前兩部分拼接,用密鑰和算法加密生成簽名。
3. JWT 的認證流程
用戶登錄:客戶端提交用戶名、密碼。
服務端驗證:驗證通過后,生成 JWT(帶用戶信息、過期時間等),返回給客戶端。
客戶端存儲:通常存儲在 LocalStorage 或 Cookie 中。
后續請求:客戶端請求時在 Header 中帶上
Authorization: Bearer <token>
。服務端驗證:服務端用密鑰驗證 JWT 是否有效、是否過期、是否被篡改。
驗證通過:允許訪問資源。
4. JWT 的優點
無狀態:服務端不存儲 Session,支持分布式擴展。
跨語言:JSON 格式,兼容性強。
高性能:減少數據庫查詢,認證速度快。
信息完整:載荷可攜帶用戶信息,減少額外查詢。
5. JWT 的缺點
不可撤銷:一旦簽發,在過期前無法主動讓其失效(除非維護黑名單)。
體積較大:比 Session ID 更大(因為要攜帶簽名和用戶信息)。
安全風險:如果私鑰泄露,所有 token 都會失效。
? ?5.1 為什么說不可撤銷呢?在前端刪除jwt(前端銷毀)不就好了?
?????????用戶自己點擊 “退出登錄”,前端刪除 token,下次再訪問接口時就沒有 token 了 → 確實相當于退出了。
?????????在大多數 正常用戶行為 下,這種方式足夠。
?5.1.1 但是會有特殊情況:
(1)多端登錄 / 被動下線🔹如果一個賬號在兩臺設備登錄,管理員希望強制其中一臺下線。🔹單靠前端刪 token,另一臺設備的 token 依然有效。(2)token 泄露🔹假如 token 被別人拷貝了(比如被竊取、抓包、XSS 攻擊),那個人依然可以繼續用。🔹你本地刪掉 token 沒用,因為“壞人”還有副本。(3)黑名單/封禁需求🔹如果公司后臺管理員封禁了某個賬號,這個用戶的 token 在過期前仍然能請求接口。🔹只能靠服務端來控制 token 的立即失效。
?? ?5.2 解決辦法:
????????具體實現本文不做演示,著重介紹JWT
前端銷毀:保證用戶主動退出后,瀏覽器不再攜帶 token。
后端控制:保證遇到異常場景(黑名單、泄露、多端沖突)時,能強制失效。
方式一:Redis 黑名單
方式二:Redis 白名單(只保留最新 token)
方式三:短 token + refresh token
6. 使用場景
用戶認證(替代傳統 session)。
API 鑒權(前后端分離、微服務)。
信息安全傳輸(聲明不可篡改)。
二、Spring Boot 使用 JWT 的完整示例
這里我們用 jjwt 庫(io.jsonwebtoken:jjwt-api)來實現。
1. 添加依賴(Maven)
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version> </dependency> <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope> </dependency> <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- 支持 json 序列化 --><version>0.11.5</version><scope>runtime</scope> </dependency>
2. 工具類
JwtUtil
import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys;import java.security.Key; import java.util.Date;public class JwtUtil {// 私鑰(實際項目中放到配置文件里)private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);// 生成 tokenpublic static String generateToken(String userId, String role) {long nowMillis = System.currentTimeMillis();long expMillis = nowMillis + 3600000; // 1小時過期Date exp = new Date(expMillis);return Jwts.builder().setSubject(userId) // sub.claim("role", role) // 自定義字段.setIssuedAt(new Date(nowMillis)) // iat.setExpiration(exp) // exp.signWith(key) // 簽名.compact();}// 驗證 tokenpublic static boolean validateToken(String token) {try {Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);return true;} catch (JwtException e) {return false;}}// 獲取用戶IDpublic static String getUserId(String token) {Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();return claims.getSubject();}// 獲取角色public static String getUserRole(String token) {Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();return claims.get("role", String.class);} }
3. Controller 示例
import org.springframework.web.bind.annotation.*;@RestController @RequestMapping("/auth") public class AuthController {// 模擬登錄接口@PostMapping("/login")public String login(@RequestParam String username, @RequestParam String password) {// 假設用戶名=admin, 密碼=123456if ("admin".equals(username) && "123456".equals(password)) {// 登錄成功,生成 JWTreturn JwtUtil.generateToken("1001", "admin");}return "用戶名或密碼錯誤";}// 需要鑒權的接口@GetMapping("/check")public String check(@RequestHeader("Authorization") String token) {token = token.replace("Bearer ", "");if (JwtUtil.validateToken(token)) {return "用戶ID:" + JwtUtil.getUserId(token) +",角色:" + JwtUtil.getUserRole(token);}return "token 無效或已過期";} }
4. 請求示例
(1) 登錄獲取 Token
POST http://localhost:8080/auth/login?username=admin&password=123456
返回:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMDAxIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjkyMzQ1NjAwLCJleHAiOjE2OTIzNDkyMDB9.Tk7Q7jtwgm3d...
(2) 攜帶 Token 訪問接口
GET http://localhost:8080/auth/check Header: Authorization: Bearer <上一步返回的token>
返回:
用戶ID:1001,角色:admin
? 總結:
JWT 由 頭部 + 載荷 + 簽名 組成。
使用時,客戶端存儲 token 并在請求頭中傳遞。
Spring Boot 中可以通過 工具類 + 攔截器/過濾器 來實現鑒權。