官方demo代码如下:
地址:https://fastapi.tiangolo.com/zh/tutorial/security/oauth2-jwt/
from datetime import datetime, timedelta
from typing import Union
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel
from fastapi.staticfiles import StaticFiles
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "619164929f51e415d24afd5ed4c16ab6987a787b6b8281c048489bd25ad99cca"
ALGORITHM = "HS256" # 算法
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # 访问令牌过期分钟数
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$2b$12$WmNep8wirPPo.z0tzYr0lOZRfa3I1G6a266c.bw1HxpGjTNn5gSpe",
"disabled": False,
}
}
# token数据模型
class Token(BaseModel):
access_token: str
token_type: str
# TokenData数据模型
class TokenData(BaseModel):
username: Union[str, None] = None
# user数据模型
class User(BaseModel):
username: str
email: Union[str, None] = None
full_name: Union[str, None] = None
disabled: Union[bool, None] = None
# user数据模型,额外加入hashed_password
class UserInDB(User):
hashed_password: str
# CryptContext实例
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2PasswordBearer实例
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
app.mount('/static', StaticFiles(directory='static'), name='static')
# 校验接收的密码是否匹配存储的哈希值
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# 获取明文密码对应的加密后的密码,(可以保存到db中,供后续验证使用)
def get_password_hash(password):
return pwd_context.hash(password)
# 从db中找username
def get_user(db, username: str):
if username in db:
user_dict = db[username]
# 解包返回符合模型的数据
return UserInDB(**user_dict)
# 身份验证,并返回用户
def authenticate_user(fake_db, username: str, password: str):
# 获取user数据
user = get_user(fake_db, username)
if not user:
return False
# 验证user数据中的hashed_password与传入的password是否一致
if not verify_password(password, user.hashed_password):
return False
return user
# 创建jwt令牌
def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
# 复制传入的dict
to_encode = data.copy()
# 赋值expire
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
# 把{"exp": expire}加入字典to_encode
to_encode.update({"exp": expire})
# 获取加密后的jwt,传入需要加密的数据,密钥,加密算法
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# 根据获取的user判断:返回报错信息,还是返回user
async def get_current_user(token: str = Depends(oauth2_scheme)): # 此处依赖oauth2_scheme
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
# 对传入的token进行decode
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
# 获取username
username: str = payload.get("sub")
if username is None:
raise credentials_exception
# 校验username
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
# 从db中找username
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
# 根据获取user数据内的disabled值判断是否有效,并返回对应内容
async def get_current_active_user(current_user: User = Depends(get_current_user)): # # 此处依赖get_current_user
# 如果disabled,则返回错误信息
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token", response_model=Token) # 响应模型使用token模型
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
# form_data依赖OAuth2PasswordRequestForm
"""
OAuth2PasswordRequestForm 并不像 OAuth2PasswordBearer 一样是 FastAPI 的一个特殊的类。
OAuth2PasswordBearer 使得 FastAPI 明白它是一个安全方案。所以它得以通过这种方式添加到 OpenAPI 中。
但 OAuth2PasswordRequestForm 只是一个你可以自己编写的类依赖项,或者你也可以直接声明 Form 参数。
但是由于这是一种常见的使用场景,因此 FastAPI 出于简便直接提供了它。
"""
# 传入db数据,username,password,验证user
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
# 如果验证失败,返回错误信息
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# 设置token有效期
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
# 获取jwt令牌
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
# 返回令牌,必须包含token_type和access_token
return {"access_token": access_token, "token_type": "bearer"}
# 判断当前用户是否处于启用状态disabled
@app.get("/users/me/", response_model=User) # 响应模型使用User
async def read_users_me(current_user: User = Depends(get_current_active_user)): # 此处依赖get_current_active_user
return current_user # 返回满足User模型的数据
@app.get("/users/me/items/")
async def read_own_items(current_user: User = Depends(get_current_active_user)):
return [{"item_id": "Foo", "owner": current_user.username}]