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

FastAPI实现接口支持Basic Auth或JWT Auth二选一认证

实现FastAPI任一认证方式即可访问接口

问题出在你当前的auth_user依赖写法上:直接将两个认证函数通过Depends作为参数,FastAPI会强制要求同时通过两种认证——只要其中一个认证失败就立即返回401,并且会要求请求同时携带两种认证的凭证。

要实现"任一认证通过即可访问"的效果,你需要在同一个依赖中依次尝试两种认证逻辑,只要其中一种成功就返回用户信息,都失败再抛出401异常。以下是两种可行方案:

方案一:用try-except依次尝试认证

保留你原有的basic_logged_user和jwt_logged_user函数(它们在认证失败时会抛出HTTPException),修改auth_user函数如下:

def auth_user(db: Session = Depends(db_session)):
    # 优先尝试JWT认证
    try:
        return jwt_logged_user(db=db)
    except HTTPException:
        # JWT认证失败,尝试Basic认证
        try:
            return basic_logged_user()
        except HTTPException:
            # 两种认证均失败,返回401
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid Credentials",
                headers={"WWW-Authenticate": "Bearer, Basic"}
            )

然后修改接口的依赖:

@router.get("/")
async def get_users(db: Session = Depends(db_session), logged_user = Depends(auth_user)):
    query_users = db.query(User).all()
    return query_users

这种方式的优势是高效:只要其中一种认证成功,就不会执行另一种认证逻辑。

方案二:让认证函数返回None(而非直接抛异常)

修改两个认证函数,当认证失败时返回None,而非抛出异常:

修改Basic认证函数

def basic_logged_user(credentials: Annotated[Optional[HTTPBasicCredentials], Depends(security)] = None):
    if not credentials:
        return None
        
    current_username_bytes = credentials.username.encode("utf8")
    correct_username_bytes = settings.SESSION_LOGIN_USER.encode("utf8")
    is_correct_username = secrets.compare_digest(current_username_bytes, correct_username_bytes)
    
    current_password_bytes = credentials.password.encode("utf8")
    correct_password_bytes = settings.SESSION_LOGIN_PASS.encode("utf8")
    is_correct_password = secrets.compare_digest(current_password_bytes, correct_password_bytes)

    if not (is_correct_username and is_correct_password):
        return None
        
    return credentials.username

修改JWT认证函数

def jwt_logged_user(token: Optional[str] = Depends(utils.OAuth2_scheme), 
                    db: Session = Depends(db_session)):
    if not token:
        return None
        
    credential_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Incorrect username or password"
    )
    
    try:
        token_data = utils.verify_token(token, credential_exception)
    except HTTPException:
        return None
        
    user = db.query(User).filter(User.username == token_data.username).first()
    return user if user else None

修改auth_user依赖

def auth_user(jwt_auth = Depends(jwt_logged_user), 
              basic_auth = Depends(basic_logged_user)):
    # 只要任一认证成功,返回对应的用户信息
    valid_user = jwt_auth or basic_auth
    
    if not valid_user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid Credentials",
            headers={"WWW-Authenticate": "Bearer, Basic"}
        )
        
    return valid_user

接口的写法和你原来的一致即可:

@router.get("/")
async def get_users(db: Session = Depends(db_session), logged_user = Depends(auth_user)):
    query_users = db.query(User).all()
    return query_users

这种方式会同时执行两种认证逻辑,再判断是否有任一成功,适合需要同时处理两种认证场景的情况。

内容的提问来源于stack exchange,提问作者Henry A.A.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 22:03:24