FastAPI 教程

22.2 用户认证与权限系统

FastAPI实战教程:构建电子商务API的用户认证与权限系统

FastAPI 教程

本教程详细指导如何使用FastAPI构建一个完整的电子商务API用户认证与权限系统,涵盖JWT认证、角色权限控制等内容,适合初学者和高级开发者学习实战项目开发。

推荐工具
PyCharm专业版开发必备

功能强大的Python IDE,提供智能代码补全、代码分析、调试和测试工具,提高Python开发效率。特别适合处理列表等数据结构的开发工作。

了解更多

综合项目实战:电子商务API用户认证与权限系统

项目简介

在电子商务应用中,用户认证和权限系统是核心组成部分,用于保护用户数据和业务逻辑。本教程将带领你使用FastAPI构建一个完整的电子商务API,专注于实现用户认证(如登录、注册)和权限管理(如基于角色的访问控制)。通过这个实战项目,你将掌握FastAPI的高级特性,并应用到实际场景中。

环境准备

在开始之前,确保你的开发环境已经设置好:

  • Python 3.7或更高版本:FastAPI支持现代Python版本。

  • 安装FastAPI和相关依赖:使用pip安装所需包。

    pip install fastapi uvicorn sqlalchemy pydantic[email] python-jose[cryptography] passlib[bcrypt]
    
    • fastapiuvicorn:用于创建和运行API服务器。
    • sqlalchemy:用于ORM(对象关系映射)来操作数据库。
    • pydantic:用于数据验证和设置。
    • python-josepasslib:用于JWT认证和密码哈希。
  • 数据库:我们将使用SQLite作为示例,因为它易于设置。在生产环境中,你可能需要切换到PostgreSQL或MySQL。

项目结构

创建一个项目文件夹,并组织文件结构如下:

ecommerce_auth_project/
├── main.py           # 主要FastAPI应用文件
├── models.py         # 数据库模型定义
├── schemas.py        # Pydantic模型用于数据验证
├── auth.py           # 认证相关函数(JWT、密码哈希等)
├── dependencies.py   # 依赖注入(如获取当前用户)
├── crud.py           # 数据库操作函数
├── database.py       # 数据库连接设置
└── routers/          # 路由模块
    ├── users.py      # 用户相关路由
    └── items.py      # 商品相关路由(示例,用于权限测试)

步骤1:设置数据库模型

models.py中,定义用户和角色模型。

from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from database import Base

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True)
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)
    role_id = Column(Integer, ForeignKey("roles.id"))
    role = relationship("Role", back_populates="users")

class Role(Base):
    __tablename__ = "roles"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, unique=True, index=True)  # 如 "admin", "user"
    users = relationship("User", back_populates="role")

database.py中,设置数据库引擎和会话。

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "sqlite:///./ecommerce.db"  # 使用SQLite数据库
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

步骤2:实现用户认证

auth.py中,添加JWT认证和密码哈希功能。

from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import HTTPException, status

SECRET_KEY = "your-secret-key"  # 在生产环境中,使用环境变量安全存储
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

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

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def verify_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        email: str = payload.get("sub")
        if email is None:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
        return email
    except JWTError:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")

步骤3:创建Pydantic模型

schemas.py中,定义数据验证模型。

from pydantic import BaseModel, EmailStr
from typing import Optional

class UserCreate(BaseModel):
    email: EmailStr
    password: str
    role_id: Optional[int] = 2  # 默认角色为普通用户,假设role_id 2是"user"

class UserLogin(BaseModel):
    email: EmailStr
    password: str

class Token(BaseModel):
    access_token: str
    token_type: str

步骤4:实现CRUD操作

crud.py中,添加数据库操作函数。

from sqlalchemy.orm import Session
from models import User
from auth import get_password_hash, verify_password

def create_user(db: Session, user: UserCreate):
    hashed_password = get_password_hash(user.password)
    db_user = User(email=user.email, hashed_password=hashed_password, role_id=user.role_id)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

def get_user_by_email(db: Session, email: str):
    return db.query(User).filter(User.email == email).first()

def authenticate_user(db: Session, email: str, password: str):
    user = get_user_by_email(db, email)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

步骤5:添加依赖注入

dependencies.py中,创建获取当前用户的依赖。

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from database import get_db
from auth import verify_token
from models import User

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
    email = verify_token(token)
    user = db.query(User).filter(User.email == email).first()
    if user is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
    return user

步骤6:实现权限系统

routers/users.py中,添加用户相关路由,包括注册、登录和获取用户信息。

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from database import get_db
from schemas import UserCreate, UserLogin, Token
from crud import create_user, authenticate_user
from auth import create_access_token

router = APIRouter(prefix="/users", tags=["users"])

@router.post("/register", response_model=Token)
async def register(user: UserCreate, db: Session = Depends(get_db)):
    # 检查邮箱是否已存在
    db_user = db.query(User).filter(User.email == user.email).first()
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    new_user = create_user(db, user)
    access_token = create_access_token(data={"sub": new_user.email})
    return {"access_token": access_token, "token_type": "bearer"}

@router.post("/login", response_model=Token)
async def login(user_login: UserLogin, db: Session = Depends(get_db)):
    user = authenticate_user(db, user_login.email, user_login.password)
    if not user:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect email or password")
    access_token = create_access_token(data={"sub": user.email})
    return {"access_token": access_token, "token_type": "bearer"}

为了演示权限,创建一个routers/items.py,仅允许特定角色访问。

from fastapi import APIRouter, Depends, HTTPException, status
from dependencies import get_current_user
from models import User, Role

router = APIRouter(prefix="/items", tags=["items"])

def check_admin_permission(current_user: User = Depends(get_current_user)):
    if current_user.role.name != "admin":
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions")
    return current_user

@router.get("/")
async def read_items(current_user: User = Depends(check_admin_permission)):
    return {"message": "Only admin can view items"}

步骤7:整合并运行应用

main.py中,集成所有模块并启动服务器。

from fastapi import FastAPI
from database import engine, Base
from routers import users, items

# 创建数据库表
Base.metadata.create_all(bind=engine)

app = FastAPI(title="ECommerce Auth API", description="A user authentication and permission system for e-commerce")

# 包含路由
app.include_router(users.router)
app.include_router(items.router)

@app.get("/")
async def root():
    return {"message": "Welcome to the ECommerce Auth API"}

运行应用:

uvicorn main:app --reload

访问 http://127.0.0.1:8000/docs 查看交互式API文档。

步骤8:测试和部署

  • 测试:使用FastAPI的TestClient编写单元测试,确保认证和权限工作正常。
  • 安全考虑
    • 使用环境变量存储SECRET_KEY。
    • 在生产环境中启用HTTPS。
    • 定期更新依赖包。
  • 部署建议
    • 使用Docker容器化应用。
    • 部署到云平台如AWS、Google Cloud或Azure。
    • 设置监控和日志记录。

总结

通过本教程,你学会了如何使用FastAPI构建一个电子商务API的用户认证与权限系统。关键点包括:

  • 设置FastAPI和数据库模型。
  • 实现JWT认证和密码哈希。
  • 使用Pydantic进行数据验证。
  • 创建依赖注入来处理当前用户和权限检查。
  • 整合路由并测试API。

这个项目可以作为基础,扩展更多功能如商品管理、订单处理等。继续探索FastAPI文档以深入学习。

祝你学习愉快!如有问题,欢迎查阅FastAPI官方文档或社区资源。

开发工具推荐
Python开发者工具包

包含虚拟环境管理、代码格式化、依赖管理、测试框架等Python开发全流程工具,提高开发效率。特别适合处理复杂数据结构和算法。

获取工具包