FastAPI 全栈后端(四):认证与授权

创作者: Yardon | GitHub: github.com/YardonYan | 版本: v1.0 |



认证 vs 授权:澄清概念

  • 认证 (Authentication):你是谁?------登录凭证(密码、JWT)
  • 授权 (Authorization):你能做什么?------权限检查(管理员 vs 普通用户)

认证像身份证(证明你是谁),授权像工作证(决定你能进哪个房间)。


密码安全存储

python 复制代码
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain: str, hashed: str) -> bool:
    return pwd_context.verify(plain, hashed)

# bcrypt 会自动加盐,一次 hash 的结果每次都不同:
hash1 = hash_password("secret")    # $2b$12$...
hash2 = hash_password("secret")    # $2b$12$...(不同!)
verify_password("secret", hash1)  # True

JWT 原理与实现

JWT(JSON Web Token)把用户信息存在 Token 本身,服务器不需要存:

复制代码
header.payload.signature
python 复制代码
from datetime import datetime, timedelta
from jose import JWTError, jwt

SECRET_KEY = "your-secret-key-change-this"
ALGORITHM = "HS256"

def create_access_token(data: dict, expires_delta: timedelta = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta or timedelta(hours=24))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

def verify_token(token: str) -> dict:
    try:
        return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    except JWTError:
        raise HTTPException(status_code=401, detail="无效的 Token")

OAuth2 密码模式

python 复制代码
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi import Depends

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")

@app.post("/auth/token")
async def login(form: OAuth2PasswordRequestForm = Depends()):
    user = await authenticate(form.username, form.password)
    if not user:
        raise HTTPException(status_code=401, detail="用户名或密码错误")
    
    access_token = create_access_token({"sub": user.username, "role": user.role})
    return {"access_token": access_token, "token_type": "bearer"}

# 在需要认证的路由中使用
@app.get("/users/me")
async def read_current_user(token: str = Depends(oauth2_scheme)):
    payload = verify_token(token)
    username = payload.get("sub")
    user = await get_user_by_username(username)
    return user

依赖注入实现权限控制

python 复制代码
async def get_current_user(token: str = Depends(oauth2_scheme)):
    payload = verify_token(token)
    username = payload.get("sub")
    user = await get_user_by_username(username)
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
    return user

# 可选:检查管理员权限
async def require_admin(user: User = Depends(get_current_user)):
    if user.role != "admin":
        raise HTTPException(status_code=403, detail="需要管理员权限")
    return user

# 路由中使用
@app.delete("/users/{user_id}", dependencies=[Depends(require_admin)])
async def delete_user(user_id: int):
    ...

角色与权限系统

python 复制代码
from enum import Enum

class Role(str, Enum):
    ADMIN = "admin"
    MODERATOR = "moderator"
    USER = "user"

class Permission(str, Enum):
    READ = "read"
    WRITE = "write"
    DELETE = "delete"

ROLE_PERMISSIONS = {
    Role.ADMIN: [Permission.READ, Permission.WRITE, Permission.DELETE],
    Role.MODERATOR: [Permission.READ, Permission.WRITE],
    Role.USER: [Permission.READ],
}

def has_permission(user: User, permission: Permission) -> bool:
    return permission in ROLE_PERMISSIONS[Role(user.role)]

本章小结

认证用 JWT(无状态、可跨域)、依赖注入做权限控制(可组合、可测试)、bcrypt 存密码(安全)。


📌 创作者: Yardon | 🏠 个人网站: GlimmerAI.top

📖 本章是「FastAPI 全栈后端」系列的第 4 章。

🌟 欢迎大家来观看!

相关推荐
我是唐青枫1 小时前
Java Spring Data JPA 实战指南:Repository 查询、分页与实体映射
java·开发语言
持敬chijing1 小时前
Web渗透之前后端漏洞-文件包含漏洞
前端·安全·web安全·网络安全·网络攻击模型·安全威胁分析
CV艺术家2 小时前
前端免费高效的接入天气组件(天气网),控制组件的样式
前端
张忠琳2 小时前
【Go 1.26.4】(Part 2) Go 1.26.4 超深度分析 — Runtime GMP 调度器 (proc.go + runtime2.go)
开发语言·golang
如果超人不会飞2 小时前
WebMCP:当浏览器学会和 AI「说人话」,你的网页就成了智能体的游乐场
javascript
hunterandroid2 小时前
RecyclerView 进阶:DiffUtil 与列表更新
前端
_codeOH2 小时前
Vue 3 vs React 19:框架还在卷,核心原理就这些
前端·vue.js
菜鸟‍2 小时前
【论文学习】Segment Anything 分割一切
深度学习·学习·计算机视觉
the_answer2 小时前
CSS 新时代:浏览器原生能力如何重塑前端开发范式
前端