FastAPI基于OAuth2与JWT的认证部署:如何实现浏览器自动携带token访问受保护端点
浏览器端自动携带Token的实现方案
核心问题定位
当前实现的缺口是:
/tokenPOST接口仅返回JSON格式的Token,浏览器原生表单提交后无法自动存储Token- 现有认证逻辑仅从
Authorization请求头读取Token,无法自动从浏览器存储位置获取Token - 登录成功后没有重定向逻辑,也没有自动携带Token的机制
具体代码修改
1. 调整Token读取逻辑
保留原有Authorization请求头读取能力(兼容接口调试、/docs页面功能),新增从Cookie读取Token的逻辑,适配浏览器自动携带Cookie的场景:
async def get_token_from_request(request: Request, oauth2_token: str = Depends(oauth2_scheme)): # 优先从Authorization请求头获取Token,兼容原有调试场景 if oauth2_token: return oauth2_token # 其次从Cookie中读取Token return request.cookies.get("access_token") # 替换原有get_current_user的Token依赖来源,原JWT解码、用户校验逻辑完全复用 async def get_current_user(token: str = Depends(get_token_from_request)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception user = get_user(fake_users_db, username=username) if user is None: raise credentials_exception return user
2. 调整登录接口返回逻辑
认证通过后构造重定向响应,将Token写入HttpOnly Cookie,实现认证完成后自动跳转到受保护端点的效果:
from fastapi.responses import RedirectResponse @app.post("/token") async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()): user = authenticate_user(fake_users_db, form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) # 构造303重定向响应,跳转到受保护的/view端点 response = RedirectResponse(url="/view", status_code=status.HTTP_303_SEE_OTHER) # 将Token写入Cookie,设置安全属性 response.set_cookie( key="access_token", value=access_token, path="/", max_age=ACCESS_TOKEN_EXPIRE_MINUTES * 60, httponly=True, # 禁止前端JS读取Cookie,防范XSS窃取 samesite="lax", secure=False # 生产HTTPS环境需改为True ) return response
3. 调整受保护端点渲染逻辑
将原JSON返回的受保护端点改为模板渲染形式,适配浏览器页面访问场景:
@app.get("/view", response_class=HTMLResponse) async def read_view(request: Request, current_user: User = Depends(get_current_active_user)): # 将当前登录用户信息传入模板,渲染私有页面 return templates.TemplateResponse( "view.html", {"request": request, "current_user": current_user} )
生产部署注意事项
- 生产环境必须开启Cookie的
secure=True属性,仅在HTTPS链路下传输Cookie,避免Token被明文窃取 - 前后端分离跨域场景需配置跨域中间件的
allow_credentials=True,同时将Cookie的samesite属性设置为none,否则浏览器不会自动携带跨域Cookie - JWT本身是Base64编码而非加密,不要在JWT中存储密码、手机号等敏感信息
- 原有
/docs页面的调试认证功能不受影响,仍可正常通过输入用户名密码获取Token调试接口 - 不建议将Token存在
localStorage中,前端JS可随意读取,存在较高的XSS攻击窃取风险
内容的提问来源于stack exchange,提问作者cryptoknight
相关产品推荐
相关产品推荐

