You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI+JavaScript用户认证问题:登录后无法访问受保护端点

解决FastAPI中httponly Cookie认证与受保护端点的适配问题

核心问题根源

你当前使用的OAuth2PasswordBearer默认仅从Authorization: Bearer <token>请求头读取令牌,但后端将access_token存入了httponly Cookie——浏览器会自动在后续同域请求中携带该Cookie,但不会暴露给JS(防止XSS攻击),因此前端无法手动提取Cookie值添加到Authorization头,导致默认依赖校验失败。

解决方案:自定义认证依赖

放弃默认的OAuth2PasswordBearer,自定义依赖逻辑从请求Cookie中读取access_token并完成令牌校验。

代码示例

from fastapi import Depends, HTTPException, status, Request
from jose import JWTError, jwt
from pydantic import BaseModel

# 你的JWT基础配置
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"

class TokenData(BaseModel):
    username: str | None = None

async def get_current_user(request: Request):
    # 从Cookie中获取令牌
    token = request.cookies.get("access_token")
    if not token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Not authenticated",
            headers={"WWW-Authenticate": "Bearer"},
        )
    try:
        # 解码JWT令牌
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401, detail="Invalid token")
        token_data = TokenData(username=username)
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid token")
    # 这里可根据username查询用户数据库,返回完整用户对象
    return token_data

# 受保护端点使用自定义依赖
@app.get("/index")
async def index(current_user: TokenData = Depends(get_current_user)):
    return {"message": f"Welcome {current_user.username}", "path": "/index"}

前端正确处理方式

前端无需额外操作:

  • 登录请求完成后,浏览器会自动保存httponly Cookie
  • 跳转/index或发送其他同域请求时,浏览器会自动在请求中携带该Cookie
  • 只要后端认证依赖能从Cookie读取令牌,即可正常通过校验

最佳实践

  1. 坚持使用httponly Cookie:避免XSS攻击窃取令牌,安全性远高于localStorage/sessionStorage
  2. 强化Cookie配置:登录接口返回Cookie时,添加安全参数:
    from fastapi.responses import JSONResponse
    
    @app.post("/login")
    async def login(form_data: OAuth2PasswordRequestForm = Depends()):
        # 用户名密码校验逻辑...
        access_token = create_access_token(data={"sub": form_data.username})
        response = JSONResponse(content={"access_token": access_token, "token_type": "bearer"})
        response.set_cookie(
            key="access_token",
            value=access_token,
            httponly=True,
            secure=True,  # 生产环境强制开启,本地调试可暂时关闭
            samesite="strict",
            max_age=3600  # 与令牌过期时间保持一致
        )
        return response
    
  3. 兼容多令牌来源(可选):若需同时支持API客户端的Authorization头和浏览器Cookie,可修改依赖逻辑优先检查头信息:
    async def get_current_user(request: Request):
        # 先检查Authorization头
        auth_header = request.headers.get("Authorization")
        token = None
        if auth_header and auth_header.startswith("Bearer "):
            token = auth_header.split(" ")[1]
        else:
            # 再检查Cookie
            token = request.cookies.get("access_token")
        
        if not token:
            raise HTTPException(status_code=401, detail="Not authenticated")
        # 后续令牌校验逻辑...
    
  4. 开启CSRF防护:使用Cookie认证时,需结合fastapi-csrf-protect等工具开启CSRF防护,避免跨站请求伪造攻击

内容的提问来源于stack exchange,提问作者Karimai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:17:26