elasticsearch入門系列">elasticsearch入門系列
1374
2025-03-31
一、JWT 是什么?
JWT(JSON Web Token)是一種開(kāi)放標(biāo)準(zhǔn) (RFC 7519),它定義了一種緊湊且獨(dú)立的方式,用于將信息作為 JSON 對(duì)象在各方之間安全地傳輸。該信息可以進(jìn)行驗(yàn)證和信任,因?yàn)樗墙?jīng)過(guò)數(shù)字簽名的。JWT 可以使用密鑰(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公鑰/私鑰對(duì)進(jìn)行簽名。
通俗地說(shuō),JWT 的本質(zhì)就是一個(gè)字符串,它是將用戶信息保存到一個(gè) Json 字符串中,然后進(jìn)行編碼后得到一個(gè) JWT token,并且這個(gè) JWT token 帶有簽名信息,接收后可以校驗(yàn)是否被篡改,所以可以用于在各方之間安全地將信息作為 Json 對(duì)象傳輸。
二、為什么使用 JWT?
基于 token 的認(rèn)證方式相比傳統(tǒng)的 session 認(rèn)證方式更節(jié)約服務(wù)器資源,并且對(duì)移動(dòng)端和分布式更加友好。具有如下優(yōu)點(diǎn):
1.支持跨域訪問(wèn)
cookie 是無(wú)法跨域的,而 token 由于沒(méi)有用到 cookie(前提是將 token 放到請(qǐng)求頭中),所以跨域后不會(huì)存在信息丟失問(wèn)題。
2.無(wú)狀態(tài)
token 機(jī)制在服務(wù)端不需要存儲(chǔ) session 信息,因?yàn)?token 自身包含了所有登錄用戶的信息,所以可以減輕服務(wù)端壓力。
3.更適用CDN
可以通過(guò)內(nèi)容分發(fā)網(wǎng)絡(luò)請(qǐng)求服務(wù)端的所有資料。
4.更適用于移動(dòng)端
當(dāng)客戶端是非瀏覽器平臺(tái)時(shí),cookie 是不被支持的,此時(shí)采用 token 認(rèn)證方式會(huì)簡(jiǎn)單很多。
5.無(wú)需考慮CSRF
由于不再依賴 cookie,所以采用 token 認(rèn)證方式不會(huì)發(fā)生 CSRF,所以也就無(wú)需考慮 CSRF 的防御。
三、何時(shí)使用 JWT?
1.授權(quán)
這是使用 JWT 的最常見(jiàn)方案。用戶登錄后,每個(gè)后續(xù)請(qǐng)求都將包含 JWT,允許用戶訪問(wèn)該令牌允許的路由、服務(wù)和資源。單點(diǎn)登錄是當(dāng)今廣泛使用 JWT 的一項(xiàng)功能,因?yàn)樗拈_(kāi)銷很小,并且能夠跨不同域輕松使用。
2.信息交換
JWT 是在各方之間安全傳輸信息的好方法。由于 JWT 可以簽名(例如,使用公鑰/私鑰對(duì)),因此您可以確定發(fā)送方就是他們所說(shuō)的人。此外,由于簽名是使用標(biāo)頭和有效負(fù)載計(jì)算的,因此您還可以驗(yàn)證內(nèi)容是否未被篡改。
四、JWT 結(jié)構(gòu)
JWT 由三個(gè)部分組成,它們之間由點(diǎn)(.)分隔,分別是:
標(biāo)頭(header)
有效載荷(payload)
簽名(Signature)
JWT 結(jié)構(gòu)如下所示:
xxxxx.yyyyy.zzzzz
1.標(biāo)頭(header)
標(biāo)頭通常由兩部分組成:令牌的類型和正在使用的簽名算法(如 HMAC SHA256 或 RSA)。例如:
{
“alg”: “HS256”,
“typ”: “JWT”
}
使用 Base64 URL 算法將該 JSON 對(duì)象轉(zhuǎn)換為字符串保存,形成 JWT 的第一部分。
2.有效載荷(payload)
JWT 的第二部分是有效負(fù)載,其中包含聲明。聲明是關(guān)于實(shí)體(通常是用戶)和其他數(shù)據(jù)的語(yǔ)句。有三種類型的聲明:注冊(cè)聲明、公共聲明和私人聲明。
注冊(cè)聲明:這些是一組預(yù)定義的聲明,這些聲明不是必需的,但建議提供一組有用的、可互操作的聲明。其中一些是:iss(發(fā)行人),exp(到期時(shí)間),sub(主題),aud(受眾)等。
請(qǐng)注意,聲明名稱的長(zhǎng)度只有三個(gè)字符,因?yàn)?JWT 應(yīng)該是緊湊的。
公共聲明:這些可以由使用JWT的人隨意定義。
私人聲明:這些是為在同意使用它們的各方之間共享信息而創(chuàng)建的自定義聲明,既不是注冊(cè)聲明也不是公開(kāi)聲明。
以下是有效負(fù)載的示例:
{
“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}
然后對(duì)有效負(fù)載進(jìn)行 Base64 URL 編碼,形成 JWT 的第二部分。
請(qǐng)注意,對(duì)于已簽名的令牌,此信息雖然受到保護(hù)以防止篡改,但任何人都可以讀取。不要將機(jī)密信息放在 JWT 的有效負(fù)載或標(biāo)頭元素中,除非它已加密。
3.簽名(Signature)
要?jiǎng)?chuàng)建簽名部分,必須獲取編碼的標(biāo)頭、編碼的有效負(fù)載、密鑰,通過(guò)指定的算法生成哈希,以確保數(shù)據(jù)不會(huì)被篡改。
例如,如果要使用 HMAC SHA256 算法,將按以下方式創(chuàng)建簽名:
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)
簽名用于驗(yàn)證消息在此過(guò)程中未發(fā)生更改。并且,對(duì)于使用私鑰簽名的令牌,它還可以驗(yàn)證 JWT 的發(fā)送者是否是它所說(shuō)的發(fā)件人。
將所有內(nèi)容放在一起,輸出是三個(gè) Base64-URL 字符串,由點(diǎn)分隔,可以在 HTML 和 HTTP 環(huán)境中輕松傳遞,同時(shí)與基于 XML 的標(biāo)準(zhǔn)(如 SAML)相比更緊湊。
五、創(chuàng)建項(xiàng)目集成 JWT 實(shí)現(xiàn) token 驗(yàn)證
1.項(xiàng)目說(shuō)明
新建 spring Initializr 項(xiàng)目 jwt,項(xiàng)目下新建controller、entity、utils 類。項(xiàng)目實(shí)現(xiàn)根據(jù)用戶信息生成 token 及驗(yàn)證 token。
項(xiàng)目目錄結(jié)構(gòu):
2.創(chuàng)建 spring Initializr 項(xiàng)目 jwt
(1).添加依賴
添加依賴,如果已按截圖操作,pom.xml 還需引入 JWT 和 lombok 依賴:
(2).添加配置
application.yml 文件中添加如下配置:
server: port: 8080 jwt: secret: qnAqsQa7600vrTBcr1WB8P8dg4cbgS5i8LZGjWnpREL # 密鑰 expiration: 30 # token 有效期(S)
PS:密鑰可通過(guò)在線工具庫(kù)生成:https://www.gjk.cn/randstr
(3).新建實(shí)體類 User
為減少不必要的代碼,引入 lombok 依賴。實(shí)體類代碼如下:
package com.chaoyue.jwt.entity; import lombok.Data; @Data public class User { // @TableId(type = IdType.AUTO) private Long id; // id private String username; // 用戶名 private String password; // 密碼 }
(4).新建 JWT 工具類 JwtUtils
package com.chaoyue.jwt.utils; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class JwtUtils { private static final String CLAIM_KEY_USERNAME = "sub"; private static final String CLAIM_KEY_CREATED = "created"; /** * 密鑰 */ @Value("${jwt.secret}") private String secret; /** * token有效期 (S) */ @Value("${jwt.expiration}") private Long expiration; /** * 根據(jù)用戶信息生成 token * @param userInfo * @return */ @SneakyThrows public String generateToken(Object userInfo) { Map
(5).新建控制類 LoginController
package com.chaoyue.jwt.controller; import com.chaoyue.jwt.entity.User; import com.chaoyue.jwt.utils.JwtUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class LoginController { @Autowired private JwtUtils jwtUtils; @GetMapping("login") public String login(User user) { // 生成token User userInfo = new User(); userInfo.setId(1L); userInfo.setUsername("admin"); userInfo.setPassword("123456"); return jwtUtils.generateToken(userInfo); } }
3.啟動(dòng)服務(wù)并測(cè)試
啟動(dòng)服務(wù)后,瀏覽器輸入:http://localhost:8080/user/login,返回:
PS:JWT 官網(wǎng):https://jwt.io/
JSON Spring Boot 通用安全
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。