Fastapi之OAuth2认证

1.基于用户名密码认证

from typing import Optional

from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

from pydantic import BaseModel
from starlette import status

tokens = APIRouter()

oauth2_schema = OAuth2PasswordBearer(tokenUrl="/token")
"""
客户端会向该接口发送一个带用户名和密码的请求,得到一个token
OAuth2PasswordBearer并不会创建相应的URL路径操做,只是指明客户端用来请求Token的地址
当请求到达时,FastAPI会检测请求的Authorization头信息,或者头信息的内容不是Bearer token,它会返回401状态码(UNAUTHORIZED)
"""

# 模拟数据库数据
fake_users_db = {
    "jack": {
        "username": "jack",
        "full_name": "Jack",
        "email": "jack@qq.com",
        "hashed_password": "d2h3k2n4h23bn4b23h53mbn3bm24mmn43",
        "disabled": True
    },
    "lucy": {
        "username": "lucy",
        "full_name": "Lucy",
        "email": "lucy@qq.com",
        "hashed_password": "9d34lk2n6h23le4b23h53mbn3bm24mg2nhk",
        "disabled": False
    }
}


class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None


class UserInDB(User):
    hashed_password: str


@tokens.post("/", summary="获取Token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user_dict = fake_users_db.get(form_data.username)  # 从数据库获取用户信息
    if not user_dict:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User does not exists"
        )
    user = UserInDB(**user_dict)
    print(user.hashed_password)
    hashed_password = fake_hash_password(form_data.password)  # 将前端提交密码进行加密后与数据库密码比对
    if hashed_password != user.hashed_password:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Incorrect username or password"
        )
    return {"access_token": user.username, "token_type": "bearer"}

2.基于token获取用户信息

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from starlette import status

tokens = APIRouter()


# 模拟加密函数
pwd_factory = {
    "password1": "d2h3k2n4h23bn4b23h53mbn3bm24mmn43",
    "password2": "9d34lk2n6h23le4b23h53mbn3bm24mg2nhk"
}


# 模拟加密
def fake_hash_password(password: str):
    return pwd_factory[password]


def get_user(username: str):
    """
    获取用户信息
    :param username:
    :return:
    """
    # 测试数据token直接使用的就是用户名,所以这块直接当作用户名使用,来查找用户
    # 真实情况下可能会使用混合加密,将用户名作为载体混淆在token信息中,需要解析出用户名后再查找用户
    if username:
        user_dict = fake_users_db[username]
        return User(**user_dict)


def fake_decode_token(token):
    """从token中解析用户信息"""
    user = get_user(token)
    return user


async def get_current_user(token: str = Depends(oauth2_schema)):
    """依赖OAuth2认证接口"""
    user = fake_decode_token(token)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"}
        )
    return user


async def get_active_user(current_user: User = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Inactive user"
        )
    return current_user


@tokens.get("/current_users", summary="获取当前用户")
async def read_user_info(current_user: User = Depends(get_active_user)):
    """获取活跃的用户"""
    return current_user
posted @ 2023-04-28 14:26  fatpuffer  阅读(402)  评论(0编辑  收藏  举报