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

FastAPI中如何让Google认证用户访问依赖本地JWT的受保护端点?

解决方法:让受保护路由兼容本地JWT和Google认证

有两种可行方案,优先推荐第一种,因为不需要改动现有受保护路由的逻辑,成本最低。


方案一:Google登录后签发本地JWT(推荐)

核心思路是:用户通过Google授权后,把Google返回的用户信息同步到本地用户体系,然后给用户签发和本地登录完全一致的JWT。这样前端后续请求用这个JWT访问受保护路由时,现有的get_current_user完全能正常验证,不需要任何修改。

具体实现步骤

写Google登录的回调接口:

from fastapi import APIRouter, HTTPException, status
from starlette.requests import Request
from your_module import User, settings, create_access_token  # 替换成你自己的业务模块

router = APIRouter()

@router.get("/auth/google/callback")
async def google_auth_callback(request: Request):
    # 从Google获取授权令牌和用户信息
    token = await google_oauth.google.authorize_access_token(request)
    user_info = token.get("userinfo")
    
    if not user_info or not user_info.get("email"):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="无法从Google获取用户信息"
        )
    
    # 查找本地用户,不存在则创建新用户
    email = user_info["email"]
    user = await User.find_one(User.email == email)
    if not user:
        user = User(
            email=email,
            name=user_info.get("name"),
            is_active=True
        )
        await user.save()
    
    # 生成本地JWT返回给前端
    access_token = create_access_token(data={"sub": email})
    return {"access_token": access_token, "token_type": "bearer"}

(注:create_access_token是你本地登录系统里生成JWT的函数,直接复用即可)

前端拿到这个JWT后,和本地登录的使用方式完全一致——请求时在Header里带Authorization: Bearer <token>,get_current_user会自动验证,完全不用改。


方案二:修改get_current_user支持两种Token验证

如果想让前端直接用Google返回的ID Token访问受保护路由,就需要修改get_current_user,让它能同时验证本地JWT和Google的ID Token。

具体实现步骤

  1. 先安装Google认证的依赖库:
pip install google-auth
  1. 修改get_current_user函数:
import google.auth
from google.auth.transport.requests import Request as GoogleAuthRequest
from google.oauth2 import id_token
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from your_module import User, settings, ALGORITHM, TokenData

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/jwt/login")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="凭证验证失败",
        headers={"WWW-Authenticate": "Bearer"},
    )
    
    # 先尝试验证本地JWT
    try:
        payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[ALGORITHM])
        email = payload.get("sub")
        if not email:
            raise credentials_exception
        token_data = TokenData(email=email)
    except JWTError:
        # 本地JWT验证失败,再尝试验证Google ID Token
        try:
            # 验证Google的ID Token,记得替换成你的Google客户端ID
            id_info = id_token.verify_oauth2_token(
                token,
                GoogleAuthRequest(),
                settings.GOOGLE_CLIENT_ID
            )
            
            # 校验签发者是否为官方Google域名
            if id_info["iss"] not in ["accounts.google.com", "https://accounts.google.com"]:
                raise ValueError("签发者无效")
            
            email = id_info["email"]
            token_data = TokenData(email=email)
        except ValueError:
            # 两种Token都验证失败,抛出异常
            raise credentials_exception
    
    # 查找本地用户,不存在则自动创建(可选,根据业务需求调整)
    user = await User.find_one(User.email == token_data.email)
    if not user:
        user = User(
            email=email,
            name=id_info.get("name"),
            is_active=True
        )
        await user.save()
    
    return user

注意事项

  • 方案二中Google的ID Token有效期只有1小时,前端需要处理Token过期后重新登录的逻辑
  • 两种方案都要确保用户邮箱的唯一性,避免本地用户和Google用户邮箱冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 07:35:19