如何在带JWT与bcrypt加密的FastAPI应用中创建首个用户
解决FastAPI应用首个用户创建问题的几种可行方案
方案一:给注册端点加环境变量控制的免认证逻辑
核心思路是让注册接口在初始化模式下跳过JWT认证,创建完首个用户后关闭该模式即可,完全不影响正式部署的安全性。
具体实现:
- 修改你的认证依赖函数,加入环境变量判断:
from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer import os oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") async def get_current_user(token: str = Depends(oauth2_scheme)): # 检查是否开启初始化模式 if os.getenv("INIT_MODE") == "true": return None # 直接跳过认证 # 以下是你原来的JWT认证逻辑 credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无法验证凭证", headers={"WWW-Authenticate": "Bearer"}, ) # ... 你的token解析、用户查询逻辑
- 注册端点保持原有逻辑,依赖这个修改后的认证函数即可:
from fastapi import APIRouter, Depends, HTTPException from passlib.context import CryptContext from sqlalchemy.orm import Session from .database import get_db from .schemas import UserCreate from .models import User router = APIRouter() pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") @router.post("/users/") async def create_user( user: UserCreate, db: Session = Depends(get_db), current_user = Depends(get_current_user) ): # 检查邮箱是否已存在 db_user = db.query(User).filter(User.email == user.email).first() if db_user: raise HTTPException(status_code=400, detail="邮箱已注册") # 用bcrypt加密密码(和你现有逻辑一致) hashed_password = pwd_context.hash(user.password) new_user = User(email=user.email, hashed_password=hashed_password) db.add(new_user) db.commit() db.refresh(new_user) return {"email": new_user.email}
- 部署时临时开启初始化模式:
启动FastAPI容器时设置环境变量INIT_MODE=true:
docker run -e INIT_MODE=true -d --name fastapi-app --link postgres-db:postgres your-fastapi-image
用curl或Postman调用/users/接口创建首个用户后,再重启容器去掉这个环境变量即可。
方案二:写独立的初始化脚本直接操作数据库
不需要启动FastAPI应用,直接写个Python脚本连接数据库,用bcrypt加密密码后插入首个用户,适合自动化部署场景。
脚本示例(init_admin.py):
import os import bcrypt from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker # 导入你项目中的User模型 from your_project.models import User # 数据库连接地址,可通过环境变量传入 DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://db_user:db_pass@postgres-db:5432/db_name") engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) def create_admin(): db = SessionLocal() # 先检查是否已有管理员用户 admin = db.query(User).filter(User.email == "admin@yourdomain.com").first() if not admin: # 加密密码 raw_pwd = "your_admin_password" hashed_pwd = bcrypt.hashpw(raw_pwd.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') new_admin = User(email="admin@yourdomain.com", hashed_password=hashed_pwd) db.add(new_admin) db.commit() print("管理员用户创建成功") else: print("管理员用户已存在") db.close() if __name__ == "__main__": create_admin()
运行脚本:
把脚本放到项目目录,用容器运行(确保能连接到数据库容器):
docker run --rm --link postgres-db:postgres -v $(pwd):/app your-fastapi-image python /app/init_admin.py
方案三:Docker Compose中添加初始化服务
如果用Docker Compose部署,可以加一个一次性的初始化服务,自动在启动时创建首个用户,完成后自动退出。
修改docker-compose.yml:
version: '3.8' services: postgres: image: postgres:15 environment: POSTGRES_USER: db_user POSTGRES_PASSWORD: db_pass POSTGRES_DB: db_name volumes: - postgres_data:/var/lib/postgresql/data/ fastapi-app: build: . environment: DATABASE_URL: postgresql://db_user:db_pass@postgres:5432/db_name ports: - "8000:8000" depends_on: - postgres - init-admin init-admin: build: . environment: DATABASE_URL: postgresql://db_user:db_pass@postgres:5432/db_name command: python /app/init_admin.py depends_on: - postgres restart: on-failure # 确保数据库启动完成后再执行 volumes: postgres_data:
执行docker-compose up时,init-admin服务会先运行脚本创建用户,之后自动退出,不会影响后续服务运行。
内容的提问来源于stack exchange,提问作者Diegol
相关产品推荐
相关产品推荐

