目录
1、HTTP 基本身份验证(Basic Authentication)
2)当需要session验证的接口会有多个时,使用中间件来做验证。
3)当有大量接口,其中一些需要登录而另一些不需要时,可以使用FastAPI的依赖注入系统来管理这些接口的访问控制。
4、OAuth2 和 OpenID Connect在fastapi中的应用
九、安全
1、HTTP 基本身份验证(Basic Authentication)
这是最简单的身份验证形式之一,它要求客户端发送带有用户名和密码的 HTTP 头部。FastAPI 可以很容易地集成这种身份验证方式。使用 hashlib
对密码进行哈希处理以提高安全性。
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from hashlib import sha256
app = FastAPI()
security = HTTPBasic()
# 假设我们存储的是密码的哈希值
STORED_USERNAME = "john"
STORED_PASSWORD_HASH = sha256("secret".encode()).hexdigest()
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
"""获取当前用户名,并验证提供的凭据是否正确。"""
correct_username = credentials.username == STORED_USERNAME
password_hash = sha256(credentials.password.encode()).hexdigest()
correct_password = password_hash == STORED_PASSWORD_HASH
if not (correct_username and correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect user or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/users/me")
def read_user_me(current_username: str = Depends(get_current_username)):
"""获取当前用户的用户名。"""
return {"username": current_username}
请求:
import requests
from requests.auth import HTTPBasicAuth
# 定义服务器地址
BASE_URL = "http://127.0.0.1:8000"
def test_get_user_me():
# 正确的凭据
correct_credentials = HTTPBasicAuth('john', 'secret')
response = requests.get(f"{BASE_URL}/users/me", auth=correct_credentials)
print("Response with correct credentials:")
print(response.status_code, response.json())
# 错误的凭据
incorrect_credentials = HTTPBasicAuth('john', 'wrongpassword')
response = requests.get(f"{BASE_URL}/users/me", auth=incorrect_credentials)
print("Response with incorrect credentials:")
print(response.status_code, response.text)
if __name__ == "__main__":
test_get_user_me()
2、Bearer Token
Bearer token 身份验证是另一种常见的身份验证方法,通常与 OAuth 2.0 结合使用。FastAPI 提供了内置的支持来处理 bearer tokens。
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 假设我们有一个函数来验证 token 是否有效
def fake_decode_token(token):
"""模拟解码令牌的函数,检查令牌是否有效并返回用户信息"""
if token == "validtoken":
return {"username": "john", "scope": "admin"}
else:
return None
async def get_current_user(token: str = Depends(oauth2_scheme)):
"""获取当前用户,如果令牌无效则抛出未授权异常"""
user = fake_decode_token(token)
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"},
)
return user
@app.get("/users/me")
def read_user_me(current_user=Depends(get_current_user)):
"""读取当前用户的信息"""
return current_user
请求:
import requests
# 定义服务器地址
BASE_URL = "http://127.0.0.1:8000"
def test_get_user_me():
# 正确的令牌
valid_token = "validtoken"
headers = {"Authorization": f"Bearer {valid_token}"}
response = requests.get(f"{BASE_URL}/users/me", headers=headers)
print("Response with valid token:")
print(response.status_code, response.json())
# 错误的令牌
invalid_token = "invalidtoken"
headers = {"Authorization": f"Bearer {invalid_token}"}
response = requests.get(f"{BASE_URL}/users/me", headers=headers)
print("Response with invalid token:")
print(response.status_code, response.text)
if __name__ == "__main__":
test_get_user_me()
3、Session 管理
1)简单实现
from fastapi import FastAPI, Request, HTTPException, status
from fastapi.responses import JSONResponse
from itsdangerous import URLSafeTimedSerializer
import hashlib
import os
def hash_password(password: str) -> str:
# 生成随机盐
salt = os.urandom(16)
# 创建新的哈希对象
hasher = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000)
# 返回盐和哈希值的组合
return salt.hex() + hasher.hex()
def verify_password(stored_password: str, provided_password: str) -> bool:
# 从存储的密码中提取盐
salt = bytes.fromhex(stored_password[:32])
# 提取哈希值
stored_hash = stored_password[32:]
# 重新计算哈希值
hasher = hashlib.pbkdf2_hmac('sha256', provided_password.encode('utf-8'), salt, 100000)
# 比较哈希值
return hasher.hex() == stored_hash
app = FastAPI()
serializer = URLSafeTimedSerializer("SECRET_KEY")
# 假设的用户数据库
fake_users_db = {
"alice": {
"username": "alice",
"hashed_password": hash_password("secret_password")
}
}
def authenticate_user(fake_db, username: str, password: str):
user = fake_db.get(username)
if not user or not verify_password(user["hashed_password"], password):
return False
return user
@app.post("/login")
async def login(request: Request):
data = await request.json()
username = data.get('username')
password = data.get('password')
user = authenticate_user(fake_users_db, username, password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
session_token = serializer.dumps(username)
response = JSONResponse({"message": "Login successful"})
response.set_cookie(key="session_token", value=session_token, httponly=True)
return response
@app.get("/p