前言
在系统开发中,身份验证与授权是保障安全和隐私的关键。
- 身份验证:确认用户身份,确保其合法性。常用技术包括 OAuth2.0 和 JWT (JSON Web Token),可安全传递信息并降低服务器负载。
- 授权:根据用户角色决定其访问权限。例如,管理员拥有完全控制权,普通用户仅能访问特定功能。
本次主要介绍 JWT 的鉴权和授权,JWT,全称为 JSON Web Token,是一种开放标准(RFC 7519),用于在各方之间以 JSON 对象的形式安全地传输信息。JWT 通常被用作 Web 应用中的身份验证机制,也可以用于信息交换。它的结构由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),各部分之间用点(.
)分隔。
- Header:包含令牌的元信息,例如签名算法(如 HMAC SHA256 或 RSA)。这部分会被 Base64 编码。
- Payload:也称为声明(Claims),包含实际需要传输的数据,例如用户 ID、角色等。需要注意的是,虽然数据会编码,但并不加密,因此不应在 Payload 中防止敏感信息。
- Signature:用于验证消息是否在传输过程中被篡改,同时也可以验证发送方的身份。签名是对 Header 和 Payload 进行 Base64 编码后,使用指定的算法和秘钥生成的。
JWT 的主要优点在于它的无状态性,这意味着服务器不需要存储令牌的相关信息,从而减少了服务器的负担。然而,这也带来了挑战,例如令牌的撤销较为困难。因此,在实际应用中,通常会结合短期有效时间和刷新机制来管理 JWT 的生命周期。
JWT 广泛应用于单点登录(SSO)、API 授权等场景,因其简洁、跨语言支持和易于实现的特点,称为现代 Web 开发中不可或缺的一部分。
第一步:了解 FastAPI 中的身份验证与授权
在 FastAPI 中,主要有两种方式来控制访问:
- 身份验证:验证用户的身份,通常通过令牌或凭据。
- 授权:根据用户的角色或权限,确定用户可以访问哪些资源。
我们将使用 JWT 令牌实现 安全的身份验证,并限制某些端点仅对已认证的用户开放。
为什么使用 JWT 进行身份验证?
- 无状态:不需要服务器端会话。
- 安全:令牌经过签名,防止篡改。
- 广泛支持:JWT 与许多框架和库兼容。
第二部:设置 FastAPI JWT 身份验证
首先,我们需要一个用于创建和验证 JWT 令牌的包。我们安装 PyJWT 来处理 JWT。
pip install PyJWT
在 main.py
中,我们将定义一个基本设置,包含两个关键端点:
- 一个
/login
端点,用于生成 JWT 令牌。 - 一个
/protected
端点,仅允许经过身份验证的用户访问。
第三步:定义 JWT 身份验证工具
我们将创建用于生成和验证 JWT 的工具函数。SECRET_KEY
替换为生产环境中安全且唯一的密钥。
import uvicorn
import jwt
from datetime import datetime, timedelta
from fastapi import HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
SECRET_KEY = "asdffgadadaf"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_jwt_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encode_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encode_jwt
create_jwt_token
:接受数据并返回 JWT 令牌。verify_jwt_token
:解码并验证令牌,如果有效则返回有效载荷。
第四步:实现登录端点以生成 JWT 令牌
我们创建一个 /login
端点,用户可以通过提供凭据来获取 JWT。
from fastapi import HTTPException, Security, FastAPI, Depends, status
from pydantic import BaseModel
app = FastAPI()
fake_users_db = {
"johndoe": {"username": "johndoe", "password": "secretpassword"}
}
class Login(BaseModel):
username: str
password: str
@app.post("/login")
async def login(user: Login):
db_user = fake_users_db.get(user.username)
if not db_user or db_user["password"] != user.password:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的用户名或密码"
)
token_data = {"sub": user.username}
token = create_jwt_token(token_data)
return {"access_token": token, "token_type": "bearer"}
fake_users_db
:一个虚拟用户数据库/login 端点
:验证用户,并在成功时返回访问令牌。
当用户使用有效凭据登录时,它们将收到一个 JWT 令牌,该令牌必须用于访问受保护的资源。
第五步:使用 JWT 身份验证保护端点
为了保护端点,我们将创建一个自定义依赖项,使用 HTTPBearer
类验证 JWT 令牌。
security = HTTPBearer()
async def get_current_user(credentials: HTTPAuthorizationCredentials = Security(security)):
token = credentials.credentials
payload = verify_jwt_token(token)
if payload is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效或已过期的令牌"
)
return payload
get_current_user
:从授权头中提取并验证 JWT。HTTPBearer
:提供 Bearer 令牌安全机制,通常用于 JWT 身份验证。
现在,我们将 get_current_user
作为依赖项应用于受保护的端点。
第六步:添加受保护的端点
我们添加一个 /protected
端点,仅允许经过身份验证的用户访问
@app.get("/protected")
async def protected_route(current_user: dict = Depends(get_current_user)):
return {"message": f"你好,{current_user['sub']}! 你已通过身份验证。"}
通过 Depends(get_current_user),端点会检查用户是否经过身份验证并拥有有效令牌,然后才授予访问权限。这种设置限制了仅授权用户可以访问。
第七步:在 Swagger UI 中测试身份验证与授权
FastAPI 会自动更新 Swagger UI,以包含你的 JWT 身份验证端点:
- 访问
http://127.0.0.1:8000/docs
- 使用
/login
端点获取 JWT 令牌 - 授权:在 Swagger UI 中,使用 “授权” 按钮输入 JWT 令牌。