前言
上节内容讲了如何获取到令牌并返回,这节我们将一起学习,如何运用令牌。
处理请求头
FastAPI有两种方式处理请求头:Header和Depends
-
Header:Header是一个函数,用于声明和解析 HTTP 请求头。- 通过在路径操作函数中使用
Header装饰器,可以从请求头中提取特定信息作为参数传递给路径操作函数。 - 适用于获取单个或特定的请求头信息,例如 User-Agent、Authorization 等。
-
Depends:Depends是一个装饰器,用于声明和注入依赖项(Dependency)。- 通过在路径操作函数中使用
Depends装饰器,可以将依赖项注入到路径操作函数中,并自动处理依赖项的逻辑。 - 适用于处理更复杂的依赖关系,例如身份验证、数据库连接等。
- 可以使用预定义的依赖项,如
OAuth2PasswordBearer,也可以自定义自己的依赖项。
通过Depends实现
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="")
class Permission:
def __call__(self, token: str = Depends(oauth2_scheme)):
if not token:
raise HTTPException(status.HTTP_200_OK, "用户信息身份认证失败, 请检查")
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Could not validate credentials", headers={"WWW-Authenticate": "Bearer"})
except Exception as e:
raise e
user = UserService.query_user(user_id)
if not user:
raise Exception("用户不存在")
return user
知识复习
__call__ 是 Python 中的一个特殊方法,用于使一个对象可以像函数一样被调用。
class MyClass:
def __call__(self, *args, **kwargs):
print("Calling MyClass")
# 创建一个 MyClass 的实例
my_obj = MyClass()
# 调用 my_obj,实际上会触发 __call__ 方法
my_obj()
用户个人信息接口
@router.get("/user/info")
def user_info(user = Depends(Permission())):
return resp_success(resp_model(user_schema.User, user), msg="查询用户信息成功")
注意
当您在路径操作函数中使用 Depends 声明了一个依赖项,并将其注入到函数中时,您需要在发起请求时在 Headers 中添加一个名为 Authorization 的字段来传递访问令牌。
验证
import requests
url = "http://127.0.0.1:8000/api/v1/auth/user/info"
headers = {
"Authorization": "Bearer <your-access-token>"
}
response = requests.get(url, headers=headers)
# 输出
{
"code": 0,
"msg": "查询用户信息成功",
"data": {
"username": "admin1",
"id": 2
}
}
成功啦
通过Header实现
class Permission:
def __call__(self, token: str = Header()):
if not token:
raise HTTPException(status.HTTP_200_OK, "用户信息身份认证失败, 请检查")
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Could not validate credentials", headers={"WWW-Authenticate": "Bearer"})
except Exception as e:
raise e
user = UserService.query_user(user_id)
if not user:
raise Exception("用户不存在")
return user
注意
这种方式headers中需要包含这里定义的参数名,比如这里是token
验证
import requests
url = "http://127.0.0.1:8000/api/v1/auth/user/info"
headers = {
"token": "<your-access-token>"
}
response = requests.get(url, headers=headers)
# 输出
{
"code": 0,
"msg": "查询用户信息成功",
"data": {
"username": "admin1",
"id": 2
}
}
可以看到,也可以成功的。
根据自己测场景选择合适的方式就行。
tokenUrl
可能看官方文档时,OAuth2PasswordBearer(tokenUrl="")中tokenUrl需要设置一个获取令牌的url。那tokenUrl有啥作用呢?
tokenUrl 参数主要用于 OpenAPI(以前称为 Swagger)文档的自动生成和交互式文档的界面。它定义了一个特定的 URL,以便在文档和交互式界面中为用户提供一个简单的方式来获取访问令牌。但是,在实际的 API 请求中,FastAPI 并不强制要求使用 tokenUrl 指定的 URL 来获取令牌。
OAuth2 规定在使用「password 流程」时,客户端/用户必须将 username 和 password 字段作为表单数据发送。而且规范明确了字段必须这样命名(因此,此处不能使用 JSON)。
更改获取令牌的接口
from fastapi.security import OAuth2PasswordRequestForm
@router.post("/login")
def login(user: OAuth2PasswordRequestForm = Depends()):
user, err = UserService.login(user.username, user.password)
if err is not None:
return resp_error(USER_ERROR_A0200, msg=err, status_code=status.HTTP_401_UNAUTHORIZED)
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
data = {
"token": UserToken.create_access_token(
user.id, expires_delta=access_token_expires
),
"token_type": "bearer"
}
return resp_success(data, msg="登录成功")
OAuth2PasswordBearer(tokenUrl="/auth/login"),这样在交互式文档http://127.0.0.1:8000/docs就可以通过tokenUrl来获取令牌。