Fastapi之JWT认证

1.JWT认证

from datetime import datetime, timedelta
from passlib.context import CryptContext
from jose import JWTError, jwt

SECRET_KEY = "60a633523fb43587dc02f6bd0f22a73a1d5d0b69f9de489de39523da92a7d2da"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30


class Token(BaseModel):
    access_token: str
    token_type: str


pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
jwt_schema = OAuth2PasswordBearer(tokenUrl="/jwt")  # 认证接口地址

def verify_password(plain_password, hashed_password):
    """对密码进行校验"""
    return pwd_context.verify(plain_password, hashed_password)


def jwt_get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)


def jwt_authenticate_user(db, username: str, password: str):
    user = jwt_get_user(db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user


def create_access_token(data: dict, expire_delta: Optional[timedelta] = None):
    """创建Token"""
    to_encode = data.copy()
    if expire_delta:
        expire = datetime.utcnow() + expire_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encode_jwt = jwt.encode(claims=to_encode, key=SECRET_KEY, algorithm=ALGORITHM)
    return encode_jwt


@tokens.post("/jwt", response_model=Token, summary="JWT获取token")
async def login_jwt(form_data: OAuth2PasswordRequestForm = Depends()):
    user = jwt_authenticate_user(db=fake_users_db, username=form_data.username, password=form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"}
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    assess_token = create_access_token(data={"sub": form_data.username}, expire_delta=access_token_expires)
    return {"access_token": assess_token, "token_type": "Bearer"}


def jwt_get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)


async def jwt_get_current_user(token: str = Depends(jwt_schema)):
    """获取当前用户"""
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Can not validate credentials",
        headers={"WWW-Authenticate": "Bearer"}
    )
    try:
        # 校验Token合法性,内部会校验过期时间
        payload = jwt.decode(token=token, key=SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
        if not username:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    user = jwt_get_user(db=fake_users_db, username=username)
    if not user:
        raise credentials_exception
    return user


async def jwt_get_current_activate_user(current_user: User = Depends(jwt_get_current_user)):
    """获取当前活跃的用户"""
    if current_user.disabled:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Inactive user"
        )
    return current_user


@tokens.get("/jwt_current_user", summary="获取活跃用户")
async def jwt_read_user_info(current_user: User = Depends(jwt_get_current_activate_user)):
    """获取活跃的用户"""
    return current_user

2.JWT加权限认证

  • 将token中加入权限范围
from typing import Optional, List
from datetime import datetime, timedelta

from dynaconf import ValidationError
from fastapi import APIRouter, Depends, HTTPException, Security
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm, SecurityScopes

from pydantic import BaseModel
from starlette import status

from passlib.context import CryptContext
from jose import JWTError, jwt

tokens = APIRouter()

# 模拟数据库数据
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


SECRET_KEY = "60a633523fb43587dc02f6bd0f22a73a1d5d0b69f9de489de39523da92a7d2da"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30


class Token(BaseModel):
    access_token: str
    token_type: str


class TokenData(BaseModel):
    username: Optional[str] = None
    scopes: List[str] = []


pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
jwt_schema = OAuth2PasswordBearer(
    tokenUrl="/token/jwt",
    scopes={
        "get_admin_info": "获取管理员用户信息",
        "del_admin_info": "删除管理员用户信息",
        "get_user_info": "获取用户信息",
        "get_user_role": "获取用户所属角色信息",
        "get_user_permission": "获取用户相关的权限信息",

    })


def verify_password(plain_password, hashed_password):
    """对密码进行校验"""
    # 为了方便,这里就不对密码进行校验了!直接通过!!!只是为了方便!
    # 加密:encryption_pwd.hash(pwd)
    # return pwd_context.verify(plain_password, hashed_password)
    return True


def jwt_get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)


def jwt_authenticate_user(db, username: str, password: str):
    user = jwt_get_user(db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user


def create_access_token(data: dict, expire_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expire_delta:
        expire = datetime.utcnow() + expire_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encode_jwt = jwt.encode(claims=to_encode, key=SECRET_KEY, algorithm=ALGORITHM)
    return encode_jwt


@tokens.post("/jwt", response_model=Token, summary="获取token")
async def login_jwt(form_data: OAuth2PasswordRequestForm = Depends()):
    user = jwt_authenticate_user(db=fake_users_db, username=form_data.username, password=form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"}
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    assess_token = create_access_token(data={"sub": form_data.username, "scopes": form_data.scopes},
                                       expire_delta=access_token_expires)
    return {"access_token": assess_token, "token_type": "Bearer"}


async def jwt_get_current_user(security_scopes: SecurityScopes, token: str = Depends(jwt_schema)):
    """获取当前用户"""

    print("当前认证方案里面的作用域:", security_scopes.scope_str)
    if security_scopes.scopes:
        authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
    else:
        authenticate_value = f"Bearer"

    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Can not validate credentials",
        headers={"WWW-Authenticate": "Bearer"}
    )
    try:
        # 内部会校验过期时间
        payload = jwt.decode(token=token, key=SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
        if not username:
            raise credentials_exception

        token_scopes = payload.get("scopes", [])
        print("当前用户所属的token信息里面包含的scopes信息有:", token_scopes)
        token_data = TokenData(scopes=token_scopes, username=username)
        print("token_data", token_data)

    except (JWTError, ValidationError):
        raise credentials_exception
    user = jwt_get_user(db=fake_users_db, username=username)
    if not user:
        raise credentials_exception

    print("当前认证方案里面所有security_scopes信息有:", security_scopes.scopes)
    for scope in security_scopes.scopes:
        # 对比用户的token锁携带的用户的作用区域授权信息
        if scope not in token_data.scopes:
            # 如果不存在则返回没有权限异常信息
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Not enough permissions",
                headers={"WWW-Authenticate": authenticate_value},
            )
    return user


async def jwt_get_current_activate_user(current_user: User = Depends(jwt_get_current_user)):
    """获取当前活跃的用户"""
    if current_user.disabled:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Inactive user"
        )
    return current_user


@tokens.get("/jwt_current_user_info", response_model=User, summary="获取用户信息")
async def get_admin_info(current_user: User = Security(jwt_get_current_activate_user, scopes=["get_user_info"])):
    """获取活跃的用户"""  
    return current_user
posted @ 2022-06-27 18:19  fatpuffer  阅读(1591)  评论(0编辑  收藏  举报