一文了解 JWT Token
在實(shí)際開發(fā)中,使用令牌認(rèn)證,還有其他很多好處。掌握令牌認(rèn)證的原理和實(shí)現(xiàn)方法,是 Go 語言開發(fā)者,必備的核心技能之一。
由于 miniblog 使用 JWT Token 進(jìn)行身份認(rèn)證,為了降低學(xué)習(xí)難度并為后續(xù)代碼實(shí)現(xiàn)奠定基礎(chǔ),本節(jié)課將介紹 JWT 的核心內(nèi)容。
1. JWT 認(rèn)證流程
學(xué)習(xí) JWT 的最佳方式是通過其認(rèn)證流程理解其原理。認(rèn)證流程如下圖所示:
上圖展示了 JWT 的認(rèn)證流程,具體流程如下:
- 客戶端(通常是前端)通過用戶名和密碼進(jìn)行登錄;
- 服務(wù)端收到請(qǐng)求后會(huì)驗(yàn)證用戶名和密碼,若與數(shù)據(jù)庫(kù)記錄不一致,則認(rèn)證失敗,若一致,則認(rèn)證通過。認(rèn)證通過后,服務(wù)端會(huì)簽發(fā)一個(gè)具有有效期的 Token 并返回給客戶端;
- 客戶端接收到 Token 后會(huì)將其緩存,例如存儲(chǔ)在瀏覽器的 Cookie 或本地存儲(chǔ)中,方便下次調(diào)用時(shí)使用;
- 客戶端在之后的每次 API 請(qǐng)求中攜帶緩存的 Token;
- 服務(wù)端接收到請(qǐng)求后會(huì)驗(yàn)證請(qǐng)求中攜帶的 Token,驗(yàn)證通過后繼續(xù)處理業(yè)務(wù)邏輯并返回?cái)?shù)據(jù);
- 如果 Token 快過期,前端會(huì)調(diào)用 Token 刷新接口續(xù)期 Token,避免用戶再次登錄。之后,會(huì)使用續(xù)期后的 Token 發(fā)送 API 請(qǐng)求。
提示:Go 項(xiàng)目開發(fā)中,Token 有效期通常設(shè)置為 2 小時(shí)。
2. JWT Token 格式
在 JWT 中,Token 由 Header、Payload、Signature 三部分組成,中間用英文點(diǎn)號(hào)(.)隔開,并使用 Base64 編碼。JWT Token 示例如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzkwNzgwMDUsImlhdCI6MTczNTQ3ODAwNSwibmJmIjoxNzM1NDc4MDA1LCJ4LXVzZXItaWQiOiJ1c2VyLXc2aXJrZyJ9.GromRG7kK90UfU_Q5iOSHs_xE-zSk0e0HLHqJQUjYMU
(1) Header 介紹
JWT Token 的 Header 中包含兩部分信息:Token 的類型和 Token 所使用的加密算法。JWT Header 示例如下:
{
"typ": "JWT",
"alg": "HS256"
}
上述示例表明,Token 類型是 JWT,加密算法為 HS256(alg 支持多種加密算法)。
(2) Payload 載荷介紹
Payload 中攜帶了 Token 的具體內(nèi)容,其中包含一些標(biāo)準(zhǔn)字段,當(dāng)然也可以添加額外字段以表達(dá)更豐富的信息。這些信息可以用于更復(fù)雜的處理場(chǎng)景,例如記錄請(qǐng)求的用戶 ID、用戶名等。標(biāo)準(zhǔn)字段包括:
- iss:JWT Token 的簽發(fā)者;
- sub:主題;
- exp:JWT Token 的過期時(shí)間;
- aud:接收 JWT Token 的一方;
- iat:JWT Token 的簽發(fā)時(shí)間;
- nbf:JWT Token 的生效時(shí)間;
- jti:JWT Token 的唯一標(biāo)識(shí)(ID)。
Payload 示例如下所示:
{
"id": 2,
"userID": "user-p7q78j",
"nbf": 1527931805,
"iat": 1527931805
}
(3) Signature 簽名介紹
Signature 是 Token 的簽名部分,其生成方式如下:
- 使用 Base64 對(duì) header.payload 進(jìn)行編碼;
- 使用密鑰(Secret)對(duì)編碼后的內(nèi)容進(jìn)行加密,加密后的內(nèi)容即為 Signature。
密鑰相當(dāng)于一個(gè)密碼,存儲(chǔ)在服務(wù)端,通常通過配置文件設(shè)置密鑰的值。
最終生成的 Token 如下所示:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzkwNzgwMDUsImlhdCI6MTczNTQ3ODAwNSwibmJmIjoxNzM1NDc4MDA1LCJ4LXVzZXItaWQiOiJ1c2VyLXc2aXJrZyJ9.GromRG7kK90UfU_Q5iOSHs_xE-zSk0e0HLHqJQUjYMU
簽名后,服務(wù)端會(huì)返回生成的 Token??蛻舳嗽谙麓握?qǐng)求時(shí)會(huì)攜帶該 Token,服務(wù)端收到 Token 后會(huì)解析出 header.payload,然后使用相同的加密算法和密碼對(duì) header.payload 再次加密,并將加密后的 Token 與收到的 Token 進(jìn)行比對(duì)。如果二者相同,則驗(yàn)證通過;如果不相同,則返回 HTTP 401 Unauthorized 錯(cuò)誤。
3. JWT Token 生成示例
下述代碼展示了具體如何生成一個(gè) JWT Token,通過代碼可以詳細(xì)的了解 Token 生成的方式:
#!/bin/bash
# 定義Header
HEADER='{"alg":"HS256","typ":"JWT"}'
# 定義Payload
PAYLOAD='{"exp":1739078005,"iat":1735478005,"nbf":1735478005,"x-user-id":"user-w6irkg"}'
# 定義Secret(用于簽名)
SECRET="Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5"
# 1. Base64編碼Header
HEADER_BASE64=$(echo -n "${HEADER}" | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n')
# 2. Base64編碼Payload
PAYLOAD_BASE64=$(echo -n "${PAYLOAD}" | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n')
# 3. 拼接Header和Payload為簽名數(shù)據(jù)
SIGNING_INPUT="${HEADER_BASE64}.${PAYLOAD_BASE64}"
# 4. 使用HMAC SHA256算法生成簽名
SIGNATURE=$(echo -n "${SIGNING_INPUT}" | openssl dgst -sha256 -hmac "${SECRET}" -binary | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n')
# 5. 拼接最終的JWT Token
JWT="${SIGNING_INPUT}.${SIGNATURE}"
# 輸出JWT Token
echo "Generated JWT Token:"
echo "${JWT}"