title: 如何在FastAPI中实现权限隔离并让用户乖乖听话?

date: 2025/06/18 17:24:12

updated: 2025/06/18 17:24:12

author: cmdragon

excerpt:

权限隔离通过用户身份验证和角色判定限制系统资源访问。FastAPI实现步骤包括用户认证、角色识别和访问控制。认证机制采用OAuth2密码授权流程结合JWT令牌,通过创建角色校验依赖项实现授权系统。进阶权限控制模式包括数据级权限隔离,确保用户只能访问自己的数据。测试与验证使用TestClient进行权限测试,常见报错如401 Unauthorized、403 Forbidden和422 Validation Error均有相应解决方案。

categories:

  • 后端开发
  • FastAPI

tags:

  • FastAPI
  • 权限隔离
  • 用户认证
  • 角色识别
  • 访问控制
  • JWT令牌
  • 数据级权限


扫描二维码

关注或者微信搜一搜:编程智域 前端至全栈交流与成长

发现1000+提升效率与开发的AI工具和实用程序https://tools.cmdragon.cn/

第一章:权限隔离的核心原理

权限隔离的本质是通过用户身份验证和角色判定,限制不同用户对系统资源的访问范围。在FastAPI中,主要通过以下三个步骤实现:

  1. 用户认证:验证请求是否来自合法用户(如JWT令牌验证)
  2. 角色识别:从认证信息中提取用户角色(admin/user)
  3. 访问控制:根据角色决定是否允许执行当前操作

系统架构示意图:

客户端请求 -> [认证中间件] -> [角色依赖注入] -> [路由处理器]

第二章:认证机制实现

使用OAuth2密码授权流程结合JWT令牌:

# 安装依赖
# pip install fastapi==0.68.0 uvicorn==0.15.0 python-jose[cryptography]==3.3.0 passlib[bcrypt]==1.7.4 from fastapi import Depends, FastAPI, HTTPException
from pydantic import BaseModel
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext # 配置参数
SECRET_KEY = "your-secret-key-here"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30 class User(BaseModel):
username: str
role: str # 新增角色字段 class UserInDB(User):
hashed_password: str # 模拟数据库
fake_users_db = {
"admin": {
"username": "admin",
"hashed_password": CryptContext(schemes=["bcrypt"]).hash("secret"),
"role": "admin"
},
"user1": {
"username": "user1",
"hashed_password": CryptContext(schemes=["bcrypt"]).hash("password"),
"role": "user"
}
} # 创建JWT令牌
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) # 用户登录接口
@app.post("/login")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user_dict = fake_users_db.get(form_data.username)
if not user_dict or not pwd_context.verify(form_data.password, user_dict["hashed_password"]):
raise HTTPException(status_code=400, detail="用户名或密码错误") access_token = create_access_token(
data={"sub": user_dict["username"], "role": user_dict["role"]}
)
return {"access_token": access_token, "token_type": "bearer"}

第三章:授权系统实现

创建角色校验依赖项:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无法验证凭据",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
role: str = payload.get("role")
if username is None or role is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return {"username": username, "role": role} # 角色权限校验依赖项
def require_role(required_role: str):
def role_checker(current_user: dict = Depends(get_current_user)):
if current_user["role"] not in required_role.split(','):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="权限不足"
)
return current_user return role_checker # 管理员专属接口
@app.get("/admin/dashboard", dependencies=[Depends(require_role("admin"))])
async def admin_dashboard():
return {"message": "欢迎进入管理面板"} # 用户通用接口
@app.get("/user/profile")
async def user_profile(current_user: dict = Depends(require_role("user,admin"))):
return {"username": current_user["username"]}

第四章:进阶权限控制模式

实现数据级权限隔离(例如用户只能访问自己的订单):

def data_permission_check(resource_owner: str):
def checker(current_user: dict = Depends(get_current_user)):
if current_user["role"] != "admin" and current_user["username"] != resource_owner:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="无权访问该资源"
)
return current_user return checker @app.get("/orders/{user_id}")
async def get_orders(
user_id: str,
current_user: dict = Depends(data_permission_check(user_id))
):
# 获取订单数据的逻辑
return {"orders": [...]}

第五章:测试与验证

使用TestClient进行权限测试:

from fastapi.testclient import TestClient

client = TestClient(app)

def test_admin_access():
# 获取管理员token
token = client.post("/login", data={"username": "admin", "password": "secret"}).json()["access_token"] response = client.get(
"/admin/dashboard",
headers={"Authorization": f"Bearer {token}"}
)
assert response.status_code == 200 def test_user_access_admin_area():
token = client.post("/login", data={"username": "user1", "password": "password"}).json()["access_token"] response = client.get(
"/admin/dashboard",
headers={"Authorization": f"Bearer {token}"}
)
assert response.status_code == 403

常见报错及解决方案

错误1:401 Unauthorized

现象{"detail":"Not authenticated"}

原因:请求头未携带有效的Authorization字段

解决:检查token格式是否正确:Bearer <token>

错误2:403 Forbidden

现象{"detail":"权限不足"}

原因:用户角色不符合接口要求

解决:检查用户角色分配,确认接口的权限要求

错误3:422 Validation Error

现象:请求参数验证失败

原因:请求体格式不符合Pydantic模型定义

解决:使用Swagger文档验证请求格式,或添加中间件捕获详细错误:

@app.middleware("http")
async def validation_errors(request: Request, call_next):
try:
return await call_next(request)
except RequestValidationError as exc:
detail = {"errors": exc.errors()}
return JSONResponse(status_code=422, content=detail)

课后Quiz

问题1:当调用/admin/dashboard接口时,如何验证用户是否具有管理员权限?

答案:通过require_role("admin")依赖项检查JWT中的role字段是否为admin

问题2:如何实现用户只能访问自己创建的数据?

答案:在数据查询时添加user_id=current_user.id过滤条件,或通过数据权限依赖项验证

问题3:遇到422错误时,最有效的调试方法是什么?

答案:查看返回的错误详情,检查请求体结构与接口定义的Pydantic模型是否一致

余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:如何在FastAPI中实现权限隔离并让用户乖乖听话? | cmdragon's Blog

往期文章归档:

如何在FastAPI中实现权限隔离并让用户乖乖听话?的更多相关文章

  1. 如何在 vue 中添加权限控制管理?---vue中文社区

    前言 在一个项目中,一些功能会涉及到重要的数据管理,为了确保数据的安全,我们会在项目中加入权限来限制每个用户的操作.作为前端,我们要做的是配合后端给到的权限数据,做页面上的各种各样的限制. 需求 因为 ...

  2. android应用中增加权限判断

    android6.0系统允许用户管理应用权限,可以关闭/打开权限. 所以需要在APP中增加权限判断,以免用户关闭相应权限后,APP运行异常. 以MMS为例,在系统设置——应用——MMS——权限——&g ...

  3. 如何在ChemDraw中打出符号π

    很多人日常使用ChemDraw是一款非常优秀的化学绘图软件,在其绘制化学结构式或者反应式的过程中,常常需要添加各种符号.比如有的用户会需要输入希腊字符π,但是不知道用什么方法添加.本教程就来给大家介绍 ...

  4. 如何优雅的在 vue 中添加权限控制

    前言 在一个项目中,一些功能会涉及到重要的数据管理,为了确保数据的安全,我们会在项目中加入权限来限制每个用户的操作.作为前端,我们要做的是配合后端给到的权限数据,做页面上的各种各样的限制. 需求 因为 ...

  5. 如何在XAMPP中设置多个网站

    xampp 是一个非常方便的本地 apache + php + mysql 的调试环境,在本地安装测试 WordPress 等各种博客.论坛程序非常方便.今天我们来给大家介绍一下,如何使用 XAMPP ...

  6. 【转】如何在vmware中如何设置ip

    如何在vmware中如何设置ip 1.修改网络接口选hostonly2.虚拟机里安装vmware-tool,对鼠标和图形进行更好地支持.如果你在图形界面下,首先要切换到文本模式.右键点击桌面,打开一个 ...

  7. 如何在ubuntu中安装php

    如何在ubuntu中安装php 情衅 | 浏览 692 次 发布于2016-05-07 12:36   最佳答案   关于Ubuntu下的LAMP配置步骤: 首先要安装LAMP 就是Apache,PH ...

  8. 如何在linux中批量建立用户并设置随机密码

    Ubuntu是基于linux的免费开源操作系统,同时也是真正意义上的“多任务多用户”操作系统,既然是多用户系统,自然就涉及到创建多个用户的问题.同时由于Ubuntu系统中的root用户具有最高权限,无 ...

  9. Spring Security OAuth2 token权限隔离

    由于项目OAuth2采用了多种模式,授权码模式为第三方系统接入,密码模式用于用户登录,Client模式用于服务间调用, 所有不同的模式下的token需要用  @PreAuthorize("h ...

  10. 如何在IIS中承载WCF NetTcpBinding 服务

    这篇博客将介绍如何在IIS中承载NetTcpBinding的服务. 1. 首先准备服务代码. Contract namespace Contract { [ServiceContract] publi ...

随机推荐

  1. Docker daemon error cannot allocate memory

    Issue When getting theses errors: Jan 12 00:21:55 -- dockerd[904]: time="2018-01-12T00:21:55.32 ...

  2. 【JUC】基础总结

    多线程的思考:为什么需要run()和start()方法,只用其中一个不行吗? run()和start() run()是运行的时候就直接执行所有代码 start()相当于开启线程,并发执行,让这些线程开 ...

  3. 新建一个空的 ASP.NET Core Web Application

    前言 Visual Studio 2017 下操作 1. 新建项目 2. 新建空的 ASP.NET Core Web Application 确定后,需要一小点的时间等待依赖库载入... 3. 新建完 ...

  4. ASP.NET 简单实现数字时钟

    每隔1000毫秒(1s)获取一次当前时间 <asp:ScriptManager ID="ScriptManager_Time" runat="server" ...

  5. java学习-7-捕获异常处理

    Java使用异常来表示错误,并通过try ... catch捕获异常:1.Error错误是你的程序无能为力的,也无法捕获的,比如内存耗尽,最终会由JVM进行捕获打印出信息.几乎听天由命.但是老手程序员 ...

  6. unigui的程序编译后自动运行傻傻的手动【7】

    我们每次修改unigui程序后,一般需要编译后执行,查看效果.可是每次都要关闭杀掉服务程序,再刷新浏览器才能实现. EMB应该知道这个反人类的做法吧.实际上提供了参数配置:自动kill服务程序,自动打 ...

  7. 【FAQ】HarmonyOS SDK 闭源开放能力 — IAP Kit(6)

    1.问题描述: 支付场景,表现是在沙盒情况下所有商品都可以正常跑通,但是在非沙盒情况下,线上购买年包1800大额支付华为的 iap.createPurchas 在输完密码就会报 1001860001 ...

  8. 深入解析Tortoise-ORM关系型字段与异步查询

    title: 深入解析Tortoise-ORM关系型字段与异步查询 date: 2025/05/01 00:12:39 updated: 2025/05/01 00:12:39 author: cmd ...

  9. 【记录】C语言|获取其他文件的绝对路径转为相对路径的两种代码

    GetModuleFileName获取当前程序的绝对路径.然后对比再strcat之类的. 只能获得同一盘符下的相对路径: #include <string.h> /*pa是绝对路径,rel ...

  10. 【MOOC】华中科技大学计算机组成原理慕课答案-第六章-中央处理器(一)

    单选 1 一定不属于冯•诺依曼机体系结构必要组成部分的是()(单选) A. CPU B. RAM C. ROM √D. Cache 计算机中的ROM主要是用来存储一些系统信息,或者启动程序BIOS程序 ...