fastapi:给管理后台增加http basic登录验证

一,代码

说明:只针对admin管理后台,不影响其他子应用如api

basic的代码

# app/core/adminbasic.py
import secrets
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials

# 初始化 Basic 认证组件
security = HTTPBasic()

def verify_admin_user(credentials: HTTPBasicCredentials = Depends(security)):
    """校验管理后台的账号密码"""
    correct_username = b"admin"
    correct_password = b"secret_password_123"

    # 使用 secrets.compare_digest 防止时序攻击
    is_correct_username = secrets.compare_digest(
        credentials.username.encode("utf-8"), correct_username
    )
    is_correct_password = secrets.compare_digest(
        credentials.password.encode("utf-8"), correct_password
    )

    if not (is_correct_username and is_correct_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="账号或密码错误",
            headers={"WWW-Authenticate": "Basic"},
        )
    return credentials.username

注入到app

# app/admin/main.py
from fastapi import FastAPI, Depends
from app.admin.views import users
from app.core.adminbasic import verify_admin_user
from app.core.exceptions import register_exception_handlers
from fastapi.staticfiles import StaticFiles

from app.core.redislink import lifespan

# 创建FastAPI应用,并传入 lifespan

# 创建独立隔离的管理后台子应用
admin_app = FastAPI(
    title="管理后台系统",
    dependencies=[Depends(verify_admin_user)]  # 👈 核心:作为子应用全局依赖
)
# 注册全局异常捕获
register_exception_handlers(admin_app)

# 1. 挂载静态文件目录
# path="/static" 是浏览器访问静态文件的 URL 前缀
# directory="app/static" 是本地项目中的文件夹名称
# name="static" 是在 Jinja2 模板中引用时使用的别名
admin_app.mount("/static", StaticFiles(directory="app/static"), name="static")

# 注册路由模块

@admin_app.get("/")
def admin_home():
    return {"message": "欢迎来到管理后台首页"}

admin_app.include_router(users.router)

注意对异常处理代码,要返回相应的headers信息,否则会导致登录窗口不弹出:

    # 3. 捕获 HTTP 异常 (如: FastAPI 自带的 404, 403)
    @app.exception_handler(StarletteHTTPException)
    async def http_exception_handler(request: Request, exc: StarletteHTTPException):
        print("http_exception_handler:", exc.status_code)
        content = BaseResponse(code=exc.status_code, msg=exc.detail, data=None).model_dump()
        # 👈 核心修改:如果 exc 里面有 headers(比如 Basic 认证的 WWW-Authenticate),就透传它
        headers = getattr(exc, "headers", None) or {}
        return JSONResponse(
            status_code=exc.status_code,
            content=content,
            headers=headers  # 👈 将 headers 塞回响应中
        )

 

 

二,测试效果:

image

 

posted @ 2026-06-29 20:09  刘宏缔的架构森林  阅读(4)  评论(0)    收藏  举报