Java JWT原理及工作流程
引言
在當(dāng)代 Web 應(yīng)用與 API 開(kāi)發(fā)里,用戶認(rèn)證和授權(quán)無(wú)疑是極為關(guān)鍵的環(huán)節(jié)。JSON Web Token(JWT)作為一種輕量級(jí)的身份驗(yàn)證機(jī)制,憑借其簡(jiǎn)潔性、可擴(kuò)展性以及跨域支持等顯著優(yōu)勢(shì),在眾多前后端分離項(xiàng)目中得到了廣泛應(yīng)用。本文將全面深入地探討 JWT 的原理、工作流程,并且結(jié)合 Java 代碼示例,展示如何在一個(gè)簡(jiǎn)單的登錄系統(tǒng)中運(yùn)用 JWT 進(jìn)行認(rèn)證。
一、JWT 概述
1. 什么是 JWT
JWT 是一種用于在網(wǎng)絡(luò)應(yīng)用間安全傳遞聲明的開(kāi)放標(biāo)準(zhǔn)(RFC 7519)。它通常由三部分構(gòu)成:頭部(Header)、載荷(Payload)和簽名(Signature),并以 .
分隔,形成類(lèi)似 xxxxx.yyyyy.zzzzz
的字符串。
2. JWT 的結(jié)構(gòu)
頭部(Header):通常包含兩部分信息,令牌的類(lèi)型(通常是 JWT)和使用的簽名算法,如 HMAC SHA256 或 RSA。它是一個(gè) JSON 對(duì)象,經(jīng)過(guò) Base64Url 編碼后形成 JWT 的第一部分。
{ "alg": "HS256", "typ": "JWT" }
載荷(Payload):包含聲明(Claims),聲明是關(guān)于實(shí)體(通常是用戶)和其他數(shù)據(jù)的聲明。聲明分為三種類(lèi)型:注冊(cè)聲明、公開(kāi)聲明和私有聲明。注冊(cè)聲明是一些預(yù)定義的聲明,如 iss
(發(fā)行人)、sub
(主題)、aud
(受眾)等。載荷也是一個(gè) JSON 對(duì)象,經(jīng)過(guò) Base64Url 編碼后形成 JWT 的第二部分。
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
簽名(Signature):為了創(chuàng)建簽名部分,需要使用編碼后的頭部、編碼后的載荷、一個(gè)密鑰(secret)和頭部中指定的簽名算法。簽名用于驗(yàn)證消息在傳遞過(guò)程中沒(méi)有被更改,并且在使用私鑰簽名的情況下,還可以驗(yàn)證 JWT 的發(fā)送者的身份。
3. JWT 的優(yōu)點(diǎn)
- 無(wú)狀態(tài):JWT 是無(wú)狀態(tài)的,服務(wù)器不需要存儲(chǔ)會(huì)話信息,因此可以很容易地?cái)U(kuò)展到多個(gè)服務(wù)器或微服務(wù)架構(gòu)中。
- 跨域支持:由于 JWT 可以通過(guò) HTTP 頭部或 URL 參數(shù)傳遞,因此可以在不同的域之間安全地使用。
- 可擴(kuò)展性:可以在載荷中添加自定義聲明,以滿足不同的業(yè)務(wù)需求。
二、JWT 工作流程
1. 用戶登錄
用戶向服務(wù)器發(fā)送登錄請(qǐng)求,提供用戶名和密碼。
2. 服務(wù)器驗(yàn)證
服務(wù)器驗(yàn)證用戶的用戶名和密碼,如果驗(yàn)證成功,服務(wù)器根據(jù)用戶信息生成一個(gè) JWT。
3. 返回 JWT
服務(wù)器將生成的 JWT 返回給客戶端。
4. 客戶端存儲(chǔ)
客戶端接收到 JWT 后,將其存儲(chǔ)在本地,通常是在瀏覽器的 localStorage
或 sessionStorage
中。
5. 后續(xù)請(qǐng)求
在后續(xù)的請(qǐng)求中,客戶端將 JWT 包含在請(qǐng)求的頭部(通常是 Authorization
頭部)中發(fā)送給服務(wù)器。
6. 服務(wù)器驗(yàn)證 JWT
服務(wù)器接收到請(qǐng)求后,驗(yàn)證 JWT 的簽名和有效期。如果驗(yàn)證成功,服務(wù)器認(rèn)為用戶是合法的,并處理請(qǐng)求。
三、代碼示例:使用 JWT 實(shí)現(xiàn)登錄認(rèn)證
1. 項(xiàng)目環(huán)境
我們使用 Java 和 Spring Boot 框架來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的登錄系統(tǒng),并使用 io.jsonwebtoken
庫(kù)來(lái)處理 JWT。
2. 引入依賴
在 pom.xml
中添加以下依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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> <version>0.11.5</version> <scope>runtime</scope> </dependency> </dependencies>
3. 代碼實(shí)現(xiàn)
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.web.bind.annotation.*; import java.util.Date; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/api") public class AuthController { // 密鑰,用于簽名和驗(yàn)證 JWT private static final String SECRET_KEY = "your_secret_key"; // 有效期,設(shè)置為 30 分鐘 private static final long EXPIRATION_TIME = 30 * 60 * 1000; // 模擬用戶數(shù)據(jù)庫(kù) private static final Map<String, String> users = new HashMap<>(); static { users.put("user1", "password1"); users.put("user2", "password2"); } // 生成 JWT private String generateToken(String username) { Date now = new Date(); Date expiration = new Date(now.getTime() + EXPIRATION_TIME); return Jwts.builder() .setSubject(username) .setIssuedAt(now) .setExpiration(expiration) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } // 驗(yàn)證 JWT private Claims validateToken(String token) { try { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } catch (Exception e) { return null; } } // 登錄接口 @PostMapping("/login") public String login(@RequestBody Map<String, String> credentials) { String username = credentials.get("username"); String password = credentials.get("password"); if (users.containsKey(username) && users.get(username).equals(password)) { return generateToken(username); } return "Invalid credentials"; } // 受保護(hù)的接口 @GetMapping("/protected") public String protectedResource(@RequestHeader("Authorization") String token) { if (token == null ||!token.startsWith("Bearer ")) { return "Token is missing"; } token = token.substring(7); Claims claims = validateToken(token); if (claims != null) { return "Welcome, " + claims.getSubject(); } return "Invalid token"; } }
4. 代碼解釋
generateToken
方法:根據(jù)用戶的用戶名生成一個(gè) JWT,包含用戶名、簽發(fā)時(shí)間和有效期。validateToken
方法:驗(yàn)證 JWT 的簽名和有效期,如果驗(yàn)證成功,返回載荷信息。/login
接口:處理用戶的登錄請(qǐng)求,驗(yàn)證用戶名和密碼,如果驗(yàn)證成功,生成并返回 JWT。/protected
接口:受保護(hù)的接口,需要在請(qǐng)求頭部中包含有效的 JWT 才能訪問(wèn)。
5. 測(cè)試
你可以使用 Postman 或者其他工具來(lái)測(cè)試接口:
- 登錄請(qǐng)求:
發(fā)送一個(gè) POST 請(qǐng)求到 http://localhost:8080/api/login
,請(qǐng)求體為 {"username": "user1", "password": "password1"}
。
- 受保護(hù)的請(qǐng)求:
發(fā)送一個(gè) GET 請(qǐng)求到 http://localhost:8080/api/protected
,并在請(qǐng)求頭中添加 Authorization: Bearer <your_token>
。
四、JWT 的安全注意事項(xiàng)
- 密鑰安全:密鑰是 JWT 簽名和驗(yàn)證的關(guān)鍵,必須妥善保管,避免泄露。
- 有效期設(shè)置:合理設(shè)置 JWT 的有效期,避免過(guò)長(zhǎng)的有效期導(dǎo)致安全風(fēng)險(xiǎn)。
- 防止重放攻擊:可以使用一次性令牌或添加時(shí)間戳等方式來(lái)防止重放攻擊。
五、總結(jié)
JWT 是一種簡(jiǎn)單而強(qiáng)大的身份驗(yàn)證機(jī)制,適用于各種前后端分離的項(xiàng)目。通過(guò)本文的介紹,你應(yīng)該對(duì) JWT 的原理、工作流程和使用方法有了更深入的了解。在實(shí)際開(kāi)發(fā)中,要注意 JWT 的安全問(wèn)題,確保系統(tǒng)的安全性。希望本文能幫助你在項(xiàng)目中成功應(yīng)用 JWT 進(jìn)行登錄認(rèn)證。
到此這篇關(guān)于Java JWT詳細(xì)講解的文章就介紹到這了,更多相關(guān)Java JWT內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java類(lèi)成員訪問(wèn)權(quán)限控制知識(shí)總結(jié)
這篇文章主要介紹了Java類(lèi)成員訪問(wèn)權(quán)限控制知識(shí)總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04IDEA快速搭建Java開(kāi)發(fā)環(huán)境的教程圖解
這篇文章主要介紹了IDEA如何快速搭建Java開(kāi)發(fā)環(huán)境,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11Java基于Tcp協(xié)議的socket編程實(shí)例
這篇文章主要介紹了Java基于Tcp協(xié)議的socket編程實(shí)例,較為詳細(xì)的分析了socket編程客戶端與服務(wù)器端的具體實(shí)現(xiàn)步驟與使用技巧,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12Java8 Collectors求和功能的自定義擴(kuò)展操作
這篇文章主要介紹了Java8 Collectors求和功能的自定義擴(kuò)展操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Java基本數(shù)據(jù)類(lèi)型包裝類(lèi)原理解析
這篇文章主要介紹了Java基本數(shù)據(jù)類(lèi)型包裝類(lèi)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05java應(yīng)用開(kāi)發(fā)之JVM運(yùn)行時(shí)內(nèi)存分析
這篇文章主要介紹了java應(yīng)用開(kāi)發(fā)之JVM運(yùn)行時(shí)內(nèi)存,文中附含圖文示例內(nèi)容分析非常簡(jiǎn)要,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09在springboot中攔截器Filter中注入bean失敗問(wèn)題及解決
這篇文章主要介紹了在springboot中攔截器Filter中注入bean失敗問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解
全局異常處理是個(gè)比較重要的功能,一般在項(xiàng)目里都會(huì)用到,這篇文章主要給大家介紹了關(guān)于SpringBoot中如何統(tǒng)一接口返回與全局異常處理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09