在 FastAPI 中实现授权:保护 Web 应用程序的分步指南
在当今世界,构建安全可靠的Web应用程序比以往任何时候都更加重要。其中一个关键方面是实施适当的授权协议,以保护敏感的用户数据,并确保只有授权用户才能访问特定资源。
FastAPI 是一个现代化的、快速(高性能)的 Web 框架,用于使用 Python 3.6 及更高版本构建 API,并基于标准的 Python 类型提示。FastAPI 的主要优势之一是其内置的用户身份验证和授权支持。
FastAPI 内置了对 JSON Web Tokens (JWT) 身份验证的支持。用户通过身份验证后,即可使用其 JWT 授权其访问特定资源或执行特定操作。本指南将详细介绍如何实现这一点。以下是本指南将涵盖的内容概述。
- FastAPI 身份验证方案设置
- 数据访问保护配置
- 访问授权
This guide will build on JWT authentication in FastAPI. Yet to see the guide? then check it out, i'll wait here. Github repository is also available, so you can clone the project and begin from where we left off
FastAPI 身份验证方案设置
要访问大多数应用程序的核心功能,通常需要登录。有些应用程序可能只提供使用邮箱和密码登录的选项(本指南将使用这种方式),而有些应用程序可能除了邮箱和密码之外,还会提供使用 Google、Facebook 等账号登录的选项。这些登录选项都有各自独立的接口,用于收集用户凭据,以及一个用于验证这些凭据的流程(视图、控制器、服务等)。所有这些共同构成了一个身份验证方案:身份验证方案定义了身份验证过程所需的内容,包括:
- 要支持的身份验证类型,例如电子邮件和密码验证、社交验证等。
- 用于收集身份验证凭据的接口,例如 SPA 应用/Postman 等。
- 验证凭据并返回令牌的过程
具有不同登录选项的身份验证方案
在设置身份验证方案方面,FastAPI 的优势在于其设置的简易性。打开main.py并包含以下内容
# other imports statement at the top
from fastapi.security import OAuth2PasswordBearer
# setup authentication scheme
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
以上代码是一个身份验证方案,它会要求用户提供用户名和密码才能登录。我们导入并实例化了0Auth2PasswordBearer一个路由来处理登录过程,并将该路由作为参数传递给tokenUrl它。登录路由会在身份验证成功后生成一个 JWT 令牌。
数据访问保护配置
数据隐私是一个非常重要的话题,应该认真对待。您只想向授权用户授予数据访问权限。我们将了解如何在 FastAPI 中通过仅允许授权用户访问其个人资料来保障数据隐私。打开main.py并包含以下代码。
@app.get("/profile/{id}", response_model=UserSchema)
def profile(id:int, session:Session=Depends(get_db)):
"""Processes request to retrieve user
profile by id
"""
return user_db_services.get_user_by_id(session=session, id=id)
我们定义了一个profile可通过路由访问的视图。它接受一个路径参数,即要查看其个人资料的/profile/{id}用户IDUserSchema 。响应类型为 `ResponseType` 。视图的主体会将 ID 和一个数据库会话传递给get_user_by_id服务。此服务目前不可用,请开放services/db/users.py此权限以使其可用:
# other functions and import statements above
def get_user_by_id(session:Session, id:int):
return session.query(User).filter(User.id == id).one()
get_user_by_id该函数查询User数据库表,并筛选出 id 值与从视图中的路径参数检索到的值相似的用户profile。
目前我们尚未设置任何数据限制(稍后会详细介绍,您会发现 FastAPI 让设置变得多么简单),这意味着所有用户的个人资料数据都对公众可见。让我们来测试一下。进入项目根目录并运行docker-compose up --build -d --no-deps
NoteYou might want to run migrations to avoid database interaction errors. Alembic is smart enough and wouldn't make any migration that has already been made, so it's fine if you re-run the migration.
在浏览器中打开localhost:8000/docs,您应该会看到此视图。
signup通过点击按钮与路由交互来创建新用户try it out。用户创建完成后,响应将返回新用户的 ID id,该 ID 将用于访问个人资料页面。下图展示了响应的示例。
就我而言,新用户的 IDid是2。我们将继续使用新用户的 ID 与个人资料端点进行交互。点击try it out个人资料端点,您应该会看到一个表单字段,需要输入 ID。
点击执行后,您应该会看到与创建用户时类似的响应。
为了设置数据隐私,我们将利用之前设置的身份验证方案,将其作为参数提供给profile查看签名。
main.py -- updating profile view
@app.get("/profile/{id}", response_model=UserSchema)
def profile(
id:int,
token: str = Depends(oauth2_scheme),
session:Session=Depends(get_db)
):
"""Processes request to retrieve the requesting user
profile
"""
return user_db_services.get_user_by_id(session=session, id=id)
添加这一简单功能后,文档页面上现在应该会显示一个“授权”按钮,如下所示。
在文档中找不到预期的更新?
If you can't find the changes on the docs page or your changes not working as expected, restarting your app container service might fix the issue
docker-compose restart app
Didn't work ? leave a comment below
尝试点击任何授权按钮来授权用户将导致422 无法处理的实体错误。点击try it out之前返回用户详细信息的个人资料端点(该端点id已传递参数),现在应该返回“未授权”信息,如下图所示。
修复无法处理的实体错误
我们目前的登录视图使用令牌UserLoginSchema来捕获身份验证详细信息。虽然这并非完全错误,但从端点响应中返回的令牌无法用于与文档中需要授权的端点进行交互。因此,让我们来修复这个问题。
main.py
from fastapi.security import OAuth2PasswordRequestForm
@app.post('/login', response_model=Dict)
def login(
payload: OAuth2PasswordRequestForm = Depends(),
session: Session = Depends(get_db)
):
"""Processes user's authentication and returns a token
on successful authentication.
request body:
- username: Unique identifier for a user e.g email,
phone number, name
- password:
"""
try:
user:user_model.User = user_db_services.get_user(
session=session, email=payload.username
)
except:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid user credentials"
)
is_validated:bool = user.validate_password(payload.password)
if not is_validated:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid user credentials"
)
return user.generate_token()
我们将OAuth2PasswordRequestForm其导入并在登录函数签名中用作参数类型,payload该参数由自动注入Depends。此外,我们还修改了从数据库检索用户的方式payload.username(最初是payload.email)。以上就是所有必要的更改。
我正在构建一个简单的 API 来测试数据库。当我使用GETrequest 时一切正常,但如果我改用POST,就会422 Unprocessable Entity出错。
以下是 FastAPI 代码:
from fastapi import FastAPI
app = FastAPI()
@app.post("/")
def main(user):
return user
然后,我的请求是……
访问授权
要获得授权访问权限,首先点击“授权”按钮,此时会弹出一个包含用户名username和password密码字段的表单。输入email之前创建的用户的 ID 作为用户名,并password输入密码进行身份验证。身份验证成功后,再次点击“授权”按钮,即可看到已授予的访问权限。
现在已授予授权访问权限,与端点交互以检索用户个人资料将正常工作。
概括
在本指南中,我们探讨了授权方案及其组成要素。我们还简要介绍了数据隐私以及 FastAPI 如何简化其实现。
感谢您抽出时间阅读。FastAPI 支持设置JWT 身份验证和授权方案,您可以进一步扩展所学知识并将其应用到您的项目中。我目前正在使用这种方法来限制对对象存储的未经授权的交互。
文章来源:https://dev.to/spaceofmiah/implementing-authorization-in-fastapi-a-step-by-step-guide-for-securing-your-web-applications-3b1l






