FastAPI 核心安全功能与模板渲染的完整示:登录、CSRF、JWT、会话、认证和缓存功能

以下是一个整合 FastAPI 核心安全功能与模板渲染的完整示例,基于多个技术文档的最佳实践,包含登录、CSRF、JWT、会话、认证和缓存功能:

from datetime import datetime, timedelta
from fastapi import FastAPI, Request, Depends, HTTPException, Form
from fastapi.templating import Jinja2Templates
from fastapi.security import OAuth2PasswordBearer
from fastapi_csrf_protect import CsrfProtect
from fastapi_sessions import SessionManager
from fastapi_sessions.backends.redis import RedisBackend
from jose import jwt
from passlib.context import CryptContext
import aioredis
import os

# ===== 基础配置 =====
app = FastAPI()
templates = Jinja2Templates(directory="templates")
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")

# ===== 安全配置 =====
JWT_SECRET = os.getenv("SECRET_KEY", "your_secure_key_123")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE = timedelta(minutes=30)

# ===== Redis连接 =====
redis = aioredis.from_url("redis://localhost:6379")
session_backend = RedisBackend(redis, prefix="session:", ttl=3600)
session_manager = SessionManager(session_backend)

# ===== CSRF保护 =====
@CsrfProtect.load_config
def get_csrf_config():
    return {"secret_key": JWT_SECRET}

# ===== 用户模型 =====
class User:
    def __init__(self, username: str, password: str):
        self.username = username
        self.hashed_password = pwd_context.hash(password)

# 模拟数据库
fake_users_db = {
    "admin": User("admin", "secret123")
}

# ===== 核心功能实现 =====
def create_jwt(username: str) -> str:
    payload = {
        "sub": username,
        "exp": datetime.utcnow() + ACCESS_TOKEN_EXPIRE,
        "csrf": CsrfProtect.generate_csrf()
    }
    return jwt.encode(payload, JWT_SECRET, ALGORITHM)

async def get_current_user(
    request: Request,
    token: str = Depends(oauth2_scheme),
    csrf_protect: CsrfProtect = Depends()
):
    try:
        # 验证CSRF令牌
        await csrf_protect.validate_csrf(request)
        payload = jwt.decode(token, JWT_SECRET, algorithms=[ALGORITHM])
        username = payload.get("sub")
        if user := fake_users_db.get(username):
            return user
    except Exception:
        raise HTTPException(401, "Invalid credentials")

# ===== 路由定义 =====
@app.get("/")
async def login_page(request: Request):
    return templates.TemplateResponse("login.html", {
        "request": request,
        "csrf_token": CsrfProtect.generate_csrf()
    })

@app.post("/login")
async def login(
    request: Request,
    username: str = Form(...),
    password: str = Form(...),
    csrf_protect: CsrfProtect = Depends()
):
    # CSRF验证
    await csrf_protect.validate_csrf(request)
    
    user = fake_users_db.get(username)
    if not user or not pwd_context.verify(password, user.hashed_password):
        return templates.TemplateResponse("login.html", {
            "request": request,
            "error": "Invalid credentials"
        })
    
    # 创建会话和JWT
    session_id = session_manager.create_session(username)
    response = templates.TemplateResponse("dashboard.html", {
        "request": request,
        "user": username
    })
    
    # 设置Cookie
    response.set_cookie("session_id", session_id, httponly=True)
    response.set_cookie("access_token", create_jwt(username))
    return response

@app.get("/dashboard")
async def dashboard(
    request: Request,
    user: User = Depends(get_current_user)
):
    return templates.TemplateResponse("dashboard.html", {
        "request": request,
        "user": user.username
    })

# ===== 缓存实现 =====
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache

FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")

@app.get("/profile")
@cache(expire=300)
async def user_profile(user: User = Depends(get_current_user)):
    return {"username": user.username, "cached_at": datetime.now()}

# ===== 模板文件 =====
"""
<!-- templates/login.html -->
<html>
<head><title>Login</title></head>
<body>
  {% if error %}<p style="color:red">{{ error }}</p>{% endif %}
  <form method="post">
    <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
    <input name="username" placeholder="Username">
    <input type="password" name="password" placeholder="Password">
    <button type="submit">Login</button>
  </form>
</body>
</html>

<!-- templates/dashboard.html -->
<html>
<head><title>Dashboard</title></head>
<body>
  <h1>Welcome {{ user }}!</h1>
  <p>Session ID: {{ request.cookies.session_id }}</p>
</body>
</html>
"""

功能说明 (整合多个技术文档)

  1. 安全认证体系
    CSRF保护:使用fastapi-csrf-protect中间件,在登录表单中通过隐藏字段传递令牌
    JWT认证:令牌包含用户身份和CSRF令牌双重验证,自动刷新机制
    会话管理:Redis存储会话数据,支持多设备登录追踪

  2. 模板交互设计
    • 登录页自动注入CSRF令牌到表单
    • 错误消息动态渲染(如无效凭证提示)
    • 仪表盘展示会话信息和缓存时间

  3. 缓存优化策略
    • Redis缓存用户配置数据,减少数据库查询
    • 自动过期机制保证数据新鲜度
    • 支持条件缓存(根据用户角色差异化)

  4. 防御机制
    • 密码使用bcrypt算法加密存储
    • HTTP-only Cookie存储会话ID
    • 双重验证机制(JWT+Session)

部署建议

# 安装依赖
pip install fastapi[all] fastapi-csrf-protect fastapi-sessions python-jose[cryptography] aioredis

# 运行服务
uvicorn main:app --reload --ws-ping-interval 30

该实现综合了以下技术文档的关键点:
• JWT生成与验证流程参考网页1和网页4
• 会话管理采用网页3的Redis存储方案
• 模板交互设计结合网页5和网页7的渲染技术
• 安全策略整合网页2的中间件配置建议

可通过访问 /docs 查看自动生成的API文档,或直接通过浏览器测试登录流程。生产环境建议补充HTTPS配置和密钥轮换机制。

posted @   iTech  阅读(17)  评论(0)    收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示