PyJWT實(shí)現(xiàn)Token驗(yàn)證
JSON Web Token (JWT) 是一種基于 JSON 格式的輕量級的安全令牌,通常用于身份驗(yàn)證和信息交換。Python 的 PyJWT 是一個(gè)流行的庫,用于處理 JWT。本文將從零開始,帶你逐步學(xué)習(xí) PyJWT 的基本用法、原理以及進(jìn)階功能。
什么是 JWT
JWT 的組成
JWT 是由三部分組成的字符串:
- Header(頭部):聲明類型和簽名算法。
- Payload(載荷):存儲(chǔ)數(shù)據(jù)(通常是用戶信息或聲明)。
- Signature(簽名):對 Header 和 Payload 進(jìn)行簽名,確保數(shù)據(jù)未被篡改。
它們通過 .
分隔,整體形式為:
Header.Payload.Signature
例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsIm5hbWUiOiJKb2UiLCJyb2xlIjoiYWRtaW4ifQ.4zUHL8hfXX8E8OomNQByT3MHnfoyQh27-8r1ZjXv9Fo
JWT 的工作流程
- 生成令牌:用戶登錄時(shí),服務(wù)器驗(yàn)證用戶身份,并生成 JWT。
- 攜帶令牌:JWT 通常通過 HTTP Header 的
Authorization
字段傳遞。 - 驗(yàn)證令牌:服務(wù)器收到請求后,驗(yàn)證 JWT 的簽名是否有效,確認(rèn)信息是否未被篡改。
安裝 PyJWT
在開始使用 PyJWT 之前,先安裝它:
pip install PyJWT
生成 JWT
創(chuàng)建簡單的 JWT
下面是如何使用 PyJWT 生成一個(gè)簡單的 JWT:
import jwt import datetime # 定義密鑰和算法 SECRET_KEY = "your_secret_key" ALGORITHM = "HS256" # 生成 JWT payload = { "user_id": 123, "name": "John Doe", "role": "admin", "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1) # 設(shè)置過期時(shí)間 } token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM) print("Generated JWT:", token)
代碼說明:
payload
是載荷,其中exp
是過期時(shí)間。jwt.encode
用于生成 JWT。
解碼 JWT
要解碼生成的 JWT 并查看數(shù)據(jù):
decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) print("Decoded Payload:", decoded_payload)
檢查過期時(shí)間
如果令牌過期,會(huì)拋出 jwt.ExpiredSignatureError
異常:
try: decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) print("Decoded Payload:", decoded_payload) except jwt.ExpiredSignatureError: print("Token has expired!")
JWT 的簽名和驗(yàn)證
PyJWT 支持多種算法,如:
- 對稱算法:
HS256
,HS384
,HS512
- 非對稱算法:
RS256
,ES256
對稱加密
對稱加密使用單一密鑰簽名和驗(yàn)證:
# 對稱加密簽名 token = jwt.encode(payload, SECRET_KEY, algorithm="HS256") # 驗(yàn)證簽名 decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
非對稱加密
非對稱加密使用公鑰和私鑰。示例:
生成密鑰(假設(shè)已有 RSA 密鑰對)。
簽名和驗(yàn)證:
private_key = open("private_key.pem").read() public_key = open("public_key.pem").read() # 使用私鑰簽名 token = jwt.encode(payload, private_key, algorithm="RS256") # 使用公鑰驗(yàn)證 decoded_payload = jwt.decode(token, public_key, algorithms=["RS256"])
高級功能
自定義聲明
除了標(biāo)準(zhǔn)聲明(如 exp
、iat
),你可以添加自定義字段:
payload = { "user_id": 123, "role": "editor", "permissions": ["read", "write"], "iat": datetime.datetime.utcnow(), # 簽發(fā)時(shí)間 "exp": datetime.datetime.utcnow() + datetime.timedelta(days=1) # 過期時(shí)間 } token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
設(shè)置過期時(shí)間
exp
是 JWT 的標(biāo)準(zhǔn)聲明,用于指定過期時(shí)間。
payload = { "user_id": 123, "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15) }
令牌刷新
令牌刷新可以延長用戶登錄會(huì)話時(shí)間:
# 原令牌 token = jwt.encode(payload, SECRET_KEY, algorithm="HS256") # 解碼并刷新 decoded = jwt.decode(token, SECRET_KEY, algorithms=["HS256"], options={"verify_exp": False}) decoded["exp"] = datetime.datetime.utcnow() + datetime.timedelta(minutes=15) # 生成新令牌 refreshed_token = jwt.encode(decoded, SECRET_KEY, algorithm="HS256")
常見錯(cuò)誤及解決方法
Token 已過期
錯(cuò)誤信息:jwt.ExpiredSignatureError
解決方法:在 jwt.decode
時(shí)捕獲異常。
try: decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) except jwt.ExpiredSignatureError: print("Token has expired!")
簽名驗(yàn)證失敗
錯(cuò)誤信息:jwt.InvalidSignatureError
解決方法:確保簽名密鑰正確,并使用相同的算法。
實(shí)戰(zhàn):JWT 身份驗(yàn)證示例
以下是一個(gè)基于 Fastapi的示例,實(shí)現(xiàn)用戶登錄和令牌驗(yàn)證:
from fastapi import FastAPI, Depends, HTTPException from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm import jwt import datetime from typing import Dict, Optional # ========================== # 配置和常量 # ========================== SECRET_KEY = "your_secret_key" # JWT 密鑰 ALGORITHM = "HS256" # JWT 算法 TOKEN_EXPIRE_HOURS = 1 # 令牌有效時(shí)間(小時(shí)) # OAuth2PasswordBearer 定義,用于獲取用戶的令牌 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # ========================== # 用戶服務(wù)類 # ========================== class UserService: """ 用戶服務(wù)類:負(fù)責(zé)用戶身份驗(yàn)證和管理 """ def __init__(self): # 模擬數(shù)據(jù)庫:用戶名和密碼 self.users_db = { "admin": "password123", "user": "mypassword" } def authenticate(self, username: str, password: str) -> bool: """ 驗(yàn)證用戶名和密碼是否匹配 :param username: 用戶名 :param password: 密碼 :return: 驗(yàn)證是否成功 """ return self.users_db.get(username) == password # ========================== # JWT 工具類 # ========================== class JWTHandler: """ JWT 工具類:負(fù)責(zé)生成和驗(yàn)證 JWT """ @staticmethod def create_token(username: str, expire_hours: int = TOKEN_EXPIRE_HOURS) -> str: """ 生成 JWT :param username: 用戶名 :param expire_hours: 令牌有效時(shí)間 :return: 生成的 JWT 字符串 """ payload = { "sub": username, # 聲明主體 "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=expire_hours) # 過期時(shí)間 } return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM) @staticmethod def verify_token(token: str) -> Optional[Dict]: """ 驗(yàn)證 JWT 并解碼 :param token: JWT 字符串 :return: 解碼后的 Payload,如果無效則返回 None """ try: return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) except jwt.ExpiredSignatureError: raise HTTPException(status_code=401, detail="令牌已過期") except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail="令牌無效") # ========================== # 依賴注入類 # ========================== class Dependencies: """ 依賴注入類,用于解耦依賴 """ def __init__(self, user_service: UserService, jwt_handler: JWTHandler): self.user_service = user_service self.jwt_handler = jwt_handler def authenticate_user(self, form_data: OAuth2PasswordRequestForm) -> str: """ 驗(yàn)證用戶并生成 JWT :param form_data: 用戶提交的表單數(shù)據(jù)(包含用戶名和密碼) :return: JWT """ username = form_data.username password = form_data.password if not self.user_service.authenticate(username, password): raise HTTPException(status_code=401, detail="用戶名或密碼錯(cuò)誤") return self.jwt_handler.create_token(username) def validate_token(self, token: str) -> str: """ 驗(yàn)證 Token 并返回用戶名 :param token: JWT :return: 解碼后的用戶名 """ payload = self.jwt_handler.verify_token(token) username = payload.get("sub") if not username: raise HTTPException(status_code=401, detail="無效的令牌") return username # ========================== # 創(chuàng)建 FastAPI 應(yīng)用 # ========================== app = FastAPI() # 實(shí)例化服務(wù)類 user_service = UserService() jwt_handler = JWTHandler() dependencies = Dependencies(user_service, jwt_handler) # ========================== # 路由定義 # ========================== @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): """ 登錄接口:驗(yàn)證用戶并返回 Token :param form_data: FastAPI 提供的 OAuth2PasswordRequestForm :return: 包含 Token 的 JSON """ token = dependencies.authenticate_user(form_data) return {"access_token": token, "token_type": "bearer"} @app.get("/protected") async def protected(token: str = Depends(oauth2_scheme)): """ 受保護(hù)的接口:需要提供有效 Token 才能訪問 :param token: OAuth2PasswordBearer 自動(dòng)提取的 JWT :return: 歡迎信息 """ username = dependencies.validate_token(token) return {"message": f"歡迎回來,{username}!您的訪問已被授權(quán)。"} # ========================== # 主程序入口(僅調(diào)試使用) # ========================== if __name__ == "__main__": import uvicorn uvicorn.run(app, host="127.0.0.1", port=8000)
代碼說明
- FastAPI 的 OAuth2PasswordBearer
OAuth2PasswordBearer
是 FastAPI 提供的工具,用于從請求頭中自動(dòng)提取和驗(yàn)證 Bearer 類型的 Token。- 通過
Depends(oauth2_scheme)
可以在路由中輕松獲取 Token。
- JWT 生成和解碼
- 使用
jwt.encode
生成包含用戶信息的 JWT。 - 使用
jwt.decode
驗(yàn)證和解碼 JWT,并處理可能的異常,如令牌過期或無效。
- 使用
- 路由說明
/token
:用于登錄,驗(yàn)證用戶名和密碼,返回 Token。/protected
:受保護(hù)的接口,只有提供有效 Token 的用戶才能訪問。
- 錯(cuò)誤處理
- 如果用戶的用戶名或密碼錯(cuò)誤,會(huì)返回 401 錯(cuò)誤。
- 如果 Token 無效或過期,也會(huì)返回 401 錯(cuò)誤。
測試流程
安裝依賴 確保安裝了
fastapi
和uvicorn
:pip install fastapi uvicorn PyJWT
啟動(dòng)服務(wù) 運(yùn)行代碼后,訪問
http://127.0.0.1:8000/docs
打開 FastAPI 自動(dòng)生成的 API 文檔頁面。測試接口
- 登錄接口 使用
/token
,提交用戶名和密碼獲取 Token。 - 受保護(hù)接口 使用
/protected
,在請求頭中添加Authorization: Bearer <token>
來訪問。
- 登錄接口 使用
總結(jié)
在這篇文章中,我們深入探討了 JSON Web Token(JWT)的基本原理,尤其是如何使用 PyJWT 庫來生成、解碼和驗(yàn)證令牌。我們首先了解了 JWT 的工作機(jī)制,并學(xué)習(xí)了簽名算法及其進(jìn)階功能,以確保令牌的安全性和可靠性。此外,我們還討論了一些常見問題及其解決方法,以便幫助你更好地應(yīng)對這些在實(shí)際應(yīng)用中可能遇到的挑戰(zhàn)。最后,我們通過一個(gè)完整的 JWT 身份驗(yàn)證實(shí)例,將理論與實(shí)踐相結(jié)合,希望能為你的項(xiàng)目提供有價(jià)值的參考。掌握這些內(nèi)容后,你將能夠靈活地在自己的 Web 應(yīng)用中應(yīng)用 JWT,提升系統(tǒng)的安全性和用戶體驗(yàn)。
到此這篇關(guān)于PyJWT實(shí)現(xiàn)Token驗(yàn)證的文章就介紹到這了,更多相關(guān)PyJWT Token驗(yàn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python簡單實(shí)現(xiàn)基于SSL的IRC bot實(shí)例
這篇文章主要介紹了python簡單實(shí)現(xiàn)基于SSL的IRC bot,實(shí)例分析了IRC機(jī)器人的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-06-06Python圖像處理實(shí)現(xiàn)兩幅圖像合成一幅圖像的方法【測試可用】
這篇文章主要介紹了Python圖像處理實(shí)現(xiàn)兩幅圖像合成一幅圖像的方法,結(jié)合實(shí)例形式分析了Python使用Image.blend()接口與Image.composite()接口進(jìn)行圖像合成的相關(guān)操作技巧,需要的朋友可以參考下2019-01-01