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.
相关产品推荐
相关产品推荐

