不止于JWT:用FastAPI的Depends实现细粒度权限控制 📅 发布时间:2026/7/16 12:39:14 👁️ 浏览次数: 本文摘要很多FastAPI初学者把JWT认证当成权限控制的终点结果上线后频繁出现越权操作。本文通过一个真实的“多租户Todo”案例带你从0搭建基于角色的访问控制RBAC和数据级权限ABAC手撕权限拦截代码分享我常用的依赖封装方案。 第一部分只有JWT到底缺了啥先别急着写代码咱们捋一捋最常见的翻车现场。想象你的API是一个高档小区。JWT就像门禁卡——刷卡能进小区大门通过认证。但进门之后你能随便进别人家吗能去物业办公室调监控吗显然不行。你需要“房间钥匙”和“工作证”。很多初版代码长这样app.get(/tasks/{task_id}) def get_task(task_id: int, user: User Depends(get_current_user)): task db.query(Task).filter(Task.id task_id).first() return task # 危险没检查这个task是不是该用户的⚠️ 警告这段代码等于让张三拿着身份证就能进李四家拿东西。只要用户伪造或篡改了token里的user_id甚至只是猜到别人的ID数据就裸奔了。 第二部分细粒度权限到底“细”在哪儿咱们做权限通常分三个层级你可以对着看看自己项目到哪一步了①全局认证Authentication你是谁——JWT搞定。②角色权限RBAC你有什么身份——比如“管理员”能删除“普通用户”只能看。③数据权限ABAC/行级你对这个具体的数据有什么权限——比如“只能改自己创建的任务”或“状态为‘草稿’的文章才能编辑”。今天重点聊②和③因为这是最容易被忽略的“隐形漏洞”。️ 第三部分实战用Depends写一个“带脑子的”权限拦截器FastAPI的Depends简直是权限控制的瑞士军刀。它不仅能拿用户信息还能嵌套依赖、提前拦截请求比在路由函数里写一堆if判断优雅一万倍。 第一步给JWT加点“料”别再只存个user_id了生成token的时候把角色(role)和权限标识(permissions)塞进payload里后续查询会省很多事儿。# 登录时生成token def create_access_token(data: dict, expires_delta: timedelta None): to_encode data.copy() # 除了user_id把角色和权限列表放进去 to_encode.update({ role: user.role, perms: user.permissions # 比如 [task:create, task:delete_own] }) return jwt.encode(to_encode, SECRET_KEY, algorithmALGORITHM)⚙️ 第二步封装权限校验依赖核心干货这里咱们写一个“可配置”的权限依赖。它的工作流程是拿到token - 解析用户 - 检查角色 - 检查具体权限。只要不通过直接抛403路由函数根本不会执行。from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from typing import List, Optional oauth2_scheme OAuth2PasswordBearer(tokenUrltoken) # 模拟获取当前用户实际项目中会查DB或解码JWT async def get_current_user(token: str Depends(oauth2_scheme)): try: payload jwt.decode(token, SECRET_KEY, algorithms[ALGORITHM]) user_id: str payload.get(sub) role: str payload.get(role) perms: List payload.get(perms, []) if user_id is None: raise HTTPException(status_code401, detail无效凭证) return {id: user_id, role: role, permissions: perms} except jwt.PyJWTError: raise HTTPException(status_code401, detail无效凭证) # 重磅可组合的权限依赖 class PermissionChecker: def __init__(self, required_role: Optional[str] None, required_perm: Optional[str] None): self.required_role required_role self.required_perm required_perm def __call__(self, user: dict Depends(get_current_user)): # 角色检查RBAC if self.required_role and user.get(role) ! self.required_role: raise HTTPException( status_codestatus.HTTP_403_FORBIDDEN, detailf需要 {self.required_role} 角色 ) # 权限检查细粒度 if self.required_perm and self.required_perm not in user.get(permissions, []): raise HTTPException( status_codestatus.HTTP_403_FORBIDDEN, detailf缺少权限: {self.required_perm} ) return user # 通过检查把用户信息传给路由用 第三步在路由中使用清爽到飞起看看封装后的效果是不是比在函数里写一堆 if 舒服多了# 实例化不同的权限检查器 require_admin PermissionChecker(required_roleadmin) require_task_create PermissionChecker(required_permtask:create) require_task_delete_own PermissionChecker(required_permtask:delete_own) # 只有admin能删任何任务 app.delete(/tasks/{task_id}) async def delete_task_any(task_id: int, admin_user: dict Depends(require_admin)): # 业务逻辑直接删反正已经确认是admin return {msg: 任务已删除} # 用户只能删自己的任务这里只检查了“是否有删除自己任务的权限”具体数据还得再查 app.delete(/tasks/my/{task_id}) async def delete_my_task(task_id: int, user: dict Depends(require_task_delete_own)): task db.query(Task).filter(Task.id task_id).first() if task.owner_id ! user[id]: raise HTTPException(status_code403, detail只能删除自己的任务) db.delete(task) return {msg: 自己的任务已删除} 第四部分进阶玩法——数据级权限ABAC上面代码里还有个瑕疵require_task_delete_own只保证了“用户有删除自己任务的权限”但没保证“这个任务真的是他的”。这就是典型的数据级权限缺失。更优雅的做法是把“数据归属校验”也封装进依赖里。参考fastapi-permissions库的思路可以给每个数据模型定义一个“权限控制列表”ACLfrom fastapi_permissions import Allow, Authenticated class Task: def __acl__(self): return [ (Allow, Authenticated, view), (Allow, fuser:{self.owner_id}, edit), (Allow, role:admin, delete), ]然后在依赖里把当前用户的principals身份列表和资源的ACL进行比对。如果 “user:123” 不在允许列表里直接 403。这样就把权限逻辑和数据模型绑定了维护起来特别清晰。 第五部分这些坑我帮你踩过了 坑1把权限逻辑写在路由函数里我当时为了赶进度在每个路由里都写 if user.role ! admin: raise ...。后来权限逻辑变了要加个“超级管理员”我改了18个文件改到吐。所以一定用Depends集中管理 坑2JWT里不放权限每次都查DB虽然查DB更实时但高频接口扛不住啊。我后来折中方案核心权限放JWT只读易变权限单独查缓存Redis。 坑3忘了异常处理导致服务器500权限校验失败一定要 raise HTTPException不要 return None 或者直接 pass不然FastAPI会继续执行路由可能触发更奇怪的数据库错误。
如何快速搭建你的专属数字人?Fay框架15分钟完全指南 如何快速搭建你的专属数字人?Fay框架15分钟完全指南 【免费下载链接】Fay Fay 是一个开源的数字人类框架,集成了语言模型和数字字符。它为各种应用程序提供零售、助手和代理版本,如虚拟购物指南、广播公司、助理、服务员、教师以及基于语音或… 2026/3/31 0:04:38
SigmaStar SSD21X系列芯片:智能家居与工业控制的多场景显示解决方案 1. SigmaStar SSD21X系列芯片:智能家居与工业控制的显示利器 第一次接触SigmaStar SSD21X系列芯片是在一个智能门锁项目上。当时客户要求低成本实现高清彩色触控屏,还要支持人脸识别和远程控制。测试了几款方案后,SSD210的表现让我印象深刻—… 2026/3/31 0:03:37
智慧矿业传送带上煤矸石检测数据集VOC+YOLO格式7341张3类别 数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数):7341标注数量(xml文件个数):7341标注数量(txt文件个数):7341标注类别… 2026/3/31 0:03:06
遗传算法核心算子:交叉与变异的策略选择与实战调优 1. 遗传算法基础回顾 遗传算法(Genetic Algorithm, GA)是一种模拟自然选择和遗传机制的优化算法。它通过模拟生物进化过程中的选择、交叉和变异等操作,在解空间中寻找最优解。遗传算法的核心思想是将问题的解表示为染色体,通过不断… 2026/7/16 12:36:44
美团/饿了么多平台API并行调用场景下的线程池隔离与熔断降级策略设计 美团/饿了么多平台API并行调用场景下的线程池隔离与熔断降级策略设计 在构建聚合外卖服务(如霸王餐、CPS返利)的后端系统时,我们通常需要同时对接美团和饿了么等多个上游平台。由于这些平台API的稳定性、响应时间各不相同,且网络环… 2026/7/16 12:34:43
KMS_VL_ALL_AIO:一站式智能批量授权解决方案深度解析 KMS_VL_ALL_AIO:一站式智能批量授权解决方案深度解析 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO KMS_VL_ALL_AIO作为一款专业的Windows与Office批量授权激活工具,为技… 2026/7/16 12:32:41
wd elements se 机械硬盘,明明关闭了所有打开文件,但是无法推出,还提示下面提示,什么情况,怎么解决? wd elements se 机械硬盘,明明关闭了所有打开文件,但是无法推出,还提示下面提示,什么情况,怎么解决? 你遇到的这个提示是Windows系统判定你的WD Elements SE机械硬盘仍被系统内的进程占用,即便你手动关闭了所有打开的文件,仍有不少隐藏的后台程序、系统服务在悄悄访问硬… 2026/7/16 12:32:41
OpenClaw不是安装包,而是钉钉生态CLI工具链 1. OpenClaw不是“免费中文版软件”,而是钉钉生态中一个被误传的开源协作工具链最近在多个技术社区和钉钉开发者群看到大量标题党内容,比如“openclaw官方安装2026免费中文版原版打包一键本地部署直连钉钉教程”——这类表述本身存在三重事实性偏差&… 2026/7/16 12:32:41
Ubuntu 20.04系统优化配置全指南 1. 为什么新装Ubuntu 20.04需要优化配置刚安装完成的Ubuntu 20.04 LTS系统虽然可以立即使用,但默认配置往往无法充分发挥系统潜力。就像刚买的新房需要装修一样,系统初始状态只提供了基础功能框架。经过十多次Ubuntu版本迭代后,我发现这些优化… 2026/7/16 12:30:40
A--10 Codex Review与GitHub PR工作流实战指南:从代码审查到安全合并 摘要:本文系统讲解如何利用Codex App的Review功能与GitHub PR工作流,实现从代码修改到安全合并的完整流程。涵盖Review面板深度使用、/review命令实战、GitHub Connector配置、PR描述撰写技巧,以及常见问题排查方法。通过多个实战案例和流程图,帮助开发者建立高效的AI辅助代… 2026/7/16 0:00:26
HAM未来路线图:下一代高可用迁移技术的发展方向与展望 HAM未来路线图:下一代高可用迁移技术的发展方向与展望 【免费下载链接】ham Based on the remote memory access capability and high bandwidth of the UB, deterministic duration virtual machine live migration is achieved, addressing planned downtime issu… 2026/7/16 0:04:27
月球是否是从地球分离出去的?——容度原理解释 月球是否是从地球分离出去的?——容度原理解释一、月球起源的“三大假说”与容度原理的重新审视月球起源的三大假说——捕获说(月球是太阳系中独立的星体,被地球引力捕获)、共生说(月球与地球同时从原始星云中形成&… 2026/7/16 0:06:27
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/13 8:31:55
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/16 3:47:53
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/16 12:08:13