先拋個問題,我們在京東購物時,整個購物過程,只登錄了一次,切換頁面后,為什么沒有再次讓你輸入賬號密碼登錄?答案就是今天的主角:jwt,那他又是如何實現(xiàn)登錄態(tài)的保持呢?
JWT(JSON Web Token)是一種用于用戶身份驗證的機(jī)制,它保證了客戶端與服務(wù)端信息的安全傳輸,這種傳輸并不是加密的傳輸,而是驗證真?zhèn)蔚膫鬏敗?/span>當(dāng)用戶通過用戶名和密碼登錄系統(tǒng)后,云端就會為此用戶進(jìn)行授權(quán),為其頒發(fā)一個『身份證』:jwt,之后客戶端訪問云端資源,需要攜帶這個【身份】才能通過云端的驗證,就像我們在國內(nèi)帶著身份證才能買機(jī)票、住酒店,拿著護(hù)照才能走遍天下,身份證、護(hù)照的驗證機(jī)制就是現(xiàn)實版的JWT。eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IuajieiKseezliIsImlzQWRtaW4iOnRydWUsImlzcyI6IueggeWGnOWknOivuyIsInN1YiI6IuajieiKseezli1KV1Tku4vnu40iLCJhdWQiOlsicHJvZ3JhbW1lciJdLCJleHAiOjE3NTU2MTc0MzUsImlhdCI6MTc1NTUzMTAzNX0.N6uvzyo-MZqtu4A1jNt9qXcBLILVFpRt29cLNJXqNWM
- Header頭部:包含令牌的類型和所使用的加密算法。
- Payload負(fù)載:實際存放的數(shù)據(jù),可以自定義,主要有以下字段。
- Signature簽名,用于驗證發(fā)送者和內(nèi)容是否被篡改。簽名由header、payload、密鑰和指定的算法生成。
下面,以一張圖來展示jwt的工作流程,這樣更清晰且容易理解:
- 第二個為訪問云端資源時,在header中攜帶jwt令牌,云端對jwt進(jìn)行驗證后,返回對應(yīng)資源。
生成jwt令牌的代碼實現(xiàn)(golang)
github.com/golang-jwt/jwt/v5
var jwtKey = []byte("your_secret_key")
第二步,定義claims結(jié)構(gòu):
type Claims struct {
Username string `json:"username"`
IsAdmin bool `json:"isAdmin"`
jwt.RegisteredClaims
}
func main() {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{
Username: "棉花糖",
IsAdmin: true,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "碼農(nóng)夜讀",
Subject: "棉花糖-JWT介紹",
Audience: []string{"programmer"},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
})
tokenString, err := token.SignedString(jwtKey)
if err != nil {
panic(err)
}
println("Generated Token:", tokenString)
}
jwt的合法性校驗,如同校驗身份證號是否準(zhǔn)確一樣,如果jwt驗證通過,則可以進(jìn)行下一步的資源獲取,否則將會終止請求,golang-jwt包提供了對jwt進(jìn)行合法性校驗的api,同時當(dāng)校驗不通過時,也會給出明確的錯誤:func ValidateToken(tokenString string) (*Claims, error) {
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
if errors.Is(err, jwt.ErrInvalidKey) {
return nil, fmt.Errorf("jwt invalid")
} else if errors.Is(err, jwt.ErrTokenMalformed) {
return nil, fmt.Errorf("malformed token")
} else if errors.Is(err, jwt.ErrTokenExpired) {
return nil, fmt.Errorf("token has expired")
}
return nil, fmt.Errorf("couldn't parse token: %v", err)
}
if !token.Valid {
return nil, fmt.Errorf("token is invalid")
}
return claims, nil
}
我們可以通過claims字段獲取到生成jwt的具體內(nèi)容:如果你需要校驗用戶id是否合法,那么就可以在生成jwt時,將用戶id放入claims字段中,在解析時就可以拿到。通常,jwt的有效性校驗都是放在服務(wù)中間件中,所有請求都會對jwt進(jìn)行驗證,以此來保證數(shù)據(jù)傳輸?shù)陌踩?/span>
閱讀原文:原文鏈接
該文章在 2025/9/3 10:19:08 編輯過