Python后端权限系统01先画边界再写登录——采购申请中的身份、动作与资源周五下午工程部门的 Alice 提交了一笔设备采购。负责销售部门的 Carol 复制了审批链接页面里却出现了“同意采购”按钮另一家公司的 Mallory 把链接里的租户名称改成自己的公司意外看到了相同编号的申请。开发者第一反应往往是“隐藏按钮”“把编号改成长一点”。这两种办法都没有回答真正的问题服务端凭什么相信当前请求可以读取或改变这张申请本专题用一个独立项目 ProcureSafe 回答这个问题。它是一套可运行的教学采购系统使用 Python 3.11、FastAPI 和 SQLite。项目没有真实付款也不连接真实员工目录我们把复杂度集中在身份、组织、资源状态与事务的交叉处。十四篇文章会沿着一张采购申请从提交、审批、下单到付款的路径展开而不是先罗列所有安全名词再拼出一个谁也说不清权限的装饰器。一次请求实际上要回答四个问题认证回答“这次请求代表谁”。登录只是建立这个证据的起点后续请求仍要验证凭据有没有过期、用户有没有被停用、会话有没有撤销。把登录成功当成永久可信等于办公室门禁刷卡一次以后永远不再检查胸牌。授权回答“这个人在这个上下文里能做什么”。同一个 Bob 在工程部门可以审批下属提交的申请并不意味着他可以审批销售部门的申请。资源判断还要回答“他能不能看见这一张”。即使 Bob 的角色确实叫 manager他也不能批准自己创建的申请。状态判断则回答“现在能不能发生这个动作”采购还没审批就请求付款即使由财务发出也应该拒绝。因此权限并不是用户名到按钮集合的一张简单映射。我们要把当前用户、目标租户、数据库中的成员关系、资源所有者、资源部门、动作和当前状态一起交给服务端判断。请求体只能表达用户的意图例如“采购一台显示器”它不能宣称“我是经理”“这张申请归别人所有”或“申请已经获批”。这种输入与权威数据的区分是后面每一课的共同前提。还有一条很容易漏掉平台管理员与租户业务管理员不是同一个范围的权力。平台管理员可以维护租户元信息但没有因为职位听起来更大就自动阅读客户公司的采购内容。ProcureSafe 中 platform 能访问/platform/tenants却没有 acme 的业务成员关系。我们故意保留这条边界让“超级管理员天然看得到一切”的默认想法在第一课就接受检验。把口头需求写成能够被否定的规则如果产品需求只有“员工只能看自己的经理可以审批”不同开发者会补出不同隐含条件。员工是否能修改自己的部门经理是否能自批财务是否可以跳过下单没有明确回答测试只能证明某个成功请求碰巧成功。本项目采用以下具体约定。员工只读取自己提交的申请经理读取本部门的申请采购、财务和租户管理员能够读取本租户的申请。审批必须由同部门经理执行而且审批者不能是申请者本人。采购人员只能把已审批申请推进到已下单财务只能把已下单申请推进到已付款。租户管理员负责调整成员关系与查看审计但这个角色本身不赋予审批、下单或付款动作。请求者与上下文想执行的操作服务端应给出的结果未携带任何凭据创建 acme 采购申请401需要有效身份Aliceacme 工程员工读取自己提交的申请200返回受允许字段Bobacme 工程经理审批 Alice 的工程申请满足版本与状态时成功Carolacme 销售经理读取 Alice 的工程申请404资源对她不可见Mallory只有 beta 成员关系请求 acme 的申请接口403没有该租户成员关系platform平台管理员读取 acme 采购列表403没有业务成员关系这里的状态码有刻意的语义区别。401 表示当前凭据无法建立身份403 表示已经知道是谁但此上下文不允许这个入口或动作404 还用于隐藏对当前用户不可见的具体资源。404 不保证世界上没有那条记录它保证服务端不向这个访问者确认那条记录。后面的租户课程会把列表与详情的判断统一起来避免详情隐藏了资源、列表却泄露标题。用攻击者的输入检查设计而不是猜测攻击者的心理威胁模型不需要写成侦探故事。把外部可以控制的内容列出来检查它穿过哪些边界更容易找到可执行的测试。客户端能够修改 URL 中的租户和资源编号能够随意构造 JSON能够重放以前取得的令牌也能够跳过网页直接发 HTTP。客户端不能被假定会按我们的页面顺序操作不能被假定永远发送“正常”的部门和金额。本项目把数据库里的用户状态、成员关系、申请记录和会话状态当作服务端事实。客户端上送的 tenant 路径只用于选择要访问的组织服务端仍要查询这个用户是否属于该组织。客户端上送的 department 只是一项待校验声明必须与当前成员关系的部门一致。金额使用整数分且严格拒绝字符串、布尔值和小数避免true被当作一分钱或者浮点舍入改变审批对象。可控输入或事件如果盲信会怎样本专题的验证点请求体加上roles或owner_id普通员工伪造经理或替别人提交额外字段 422身份来自会话修改路径中的租户跨公司读取业务数据先查成员关系再按租户查询重放旧 refresh token已轮换凭据再次获得会话一次消费重放撤销整个 family浏览器自动携带 Cookie其他站点诱导用户写入Origin 与 CSRF token 双重检查同时批准同一版本一次审批变成两次状态推进事务内判断状态和版本威胁表不是产品功能清单。例如这里暂时没有实现密码找回就不能宣称已经防住找回邮件被接管。我们明确实现的边界必须能在代码和实验里找到证据没有实现的能力要保持其边界清楚。教学项目也不会因为使用了密码哈希或签名令牌就自动拥有生产系统所需的运维治理、密钥轮换和事件响应流程。从可重复的沙盒开始运行在专题根目录进入project执行python -m pip install -e .[test]安装这个独立项目及测试依赖。回到专题根目录后运行python lessons/02/answer_previous.py可以执行本课练习的完整参考答案。参考答案放在下一课目录是为了保持“先做本课问题、下一课逐项验证”的学习顺序第二篇正文也会完整嵌入该文件不需要在多个仓库之间找片段。交付环境实际运行参考答案得到以下输出。它只总结已通过的断言不展示身份凭据E01 PASS: 7 boundary checkslessons/support.py中的sandbox()为一次实验建立临时 SQLite 文件生成只在内存里使用的随机演示口令写入八个演示身份再创建 FastAPI TestClient。退出上下文后客户端和临时目录得到清理。credentials是用户名到临时口令的映射答案使用它登录不会把口令写在正文、输出或审计里。所有演示用户共享本次临时种子口令是教学夹具选择不能复制成真实企业的初始化规则。TestClient 会执行实际 ASGI 应用、校验器、权限判断和 SQLite 事务但它不经过真实网卡、代理或浏览器引擎。因此本课的证据适合回答“应用是否拒绝这个 HTTP 输入”不适合证明 TLS 配置、浏览器 SameSite 行为或反向代理头处理正确。专题末尾另有回环 HTTP 验收读报告时要分清这两类证据。先读输入契约再追完整调用链本课完整模块project/src/procuresafe/schemas.py展示了输入契约。建议先找到登录体、创建申请体和状态推进体再对应上面三类不可信输入。创建体只接收标题、金额与部门不能接收 owner、tenant、status 或角色。状态推进体要求expected_version使客户端明确说明自己依据的是哪一版申请。注意Pydantic 输入校验只能回答“形状是否允许”。金额合法并不证明 Alice 有权付款部门名称属于允许集合也不证明 Alice 属于该部门。这些规则会在数据库事实与授权服务中继续判断。把所有检查都塞进请求模型容易让校验器偷偷依赖当前用户、连接数据库最终既难测试又难解释。我们把纯输入规则留在 schema把依赖身份和资源的规则留在后面的 policy 与 service。追请求时按这个顺序观察路由解析输入会话层建立身份服务层在目标租户读取成员关系资源查询限定租户与可见范围策略判断动作事务提交业务变化与成功审计。这个顺序不是让你背函数名而是让你在一次拒绝发生时知道哪个边界承担了责任。以后新增“撤回采购”时也可以逐项问清谁能撤回、哪些状态可撤回、是否检查版本、如何留下证据。本课练习 E01把安全矩阵变成可运行请求在一个临时沙盒里先让 Alice 创建工程部门申请再依次验证五行事实匿名创建得到 401Alice 能读取自己的申请Bob 能看到本部门申请Carol 不能看到跨部门详情Mallory 不能通过 acme 入口读取列表。最后让 platform 读取平台租户信息成功同时确认它读取 acme 采购列表得到 403。练习不能只打印状态码后声称通过。每一行都要用断言记录期待值成功读取还要检查返回的申请编号确实等于刚创建的编号避免 accidentally 读到另一个演示对象。令牌只用于构造 Authorization 头不要把登录响应整体打印出来。最终输出可以是一行E01 PASS: 7 boundary checks里面不包含令牌或口令。完成后尝试用一句话解释两个反例为什么 Carol 得到 404而 Mallory 得到 403为什么平台管理员能查看租户元信息却不能查看租户采购列表。若你的解释只剩“代码就是这么写的”回到成员范围、资源可见性与管理范围三个概念重新推导。下一篇将完整给出 E01 答案并从登录的第一道证据开始数据库应该保存什么才能验证口令而不保存可直接读取的口令。本课完整输入契约fromtypingimportAnnotated,LiteralfrompydanticimportBaseModel,ConfigDict,Field,StringConstraints,model_validator DepartmentLiteral[engineering,sales]TenantRoleLiteral[employee,manager,buyer,finance,tenant_admin]MoneyAnnotated[int,Field(strictTrue,gt0,le100_000_000)]classInput(BaseModel):# 拒绝静默忽略roles/owner_id等字段让越权输入明确失败。model_configConfigDict(extraforbid)classLogin(Input):username:Annotated[str,StringConstraints(min_length1,max_length64)]password:Annotated[str,StringConstraints(min_length1,max_length1024)]classRefresh(Input):refresh_token:Annotated[str,StringConstraints(min_length1,max_length256)]classNewRequest(Input):title:Annotated[str,StringConstraints(strip_whitespaceTrue,min_length1,max_length120)]amount_cents:Money department:DepartmentclassTransition(Input):expected_version:Annotated[int,Field(strictTrue,ge1)]classMemberUpdate(Input):department:Department roles:Annotated[list[TenantRole],Field(min_length1,max_length5)]model_validator(modeafter)defunique_roles(self):iflen(self.roles)!len(set(self.roles)):raiseValueError(Roles must be unique)returnself专题目录《认证、授权与企业级权限系统课程目录》 · 下一篇《Python后端权限系统02数据库泄露之后密码还能守住什么——scrypt 与统一登录失败》
企业数字化 ERP 产品动态
相关推荐
Python后端部署排障18:从空环境到清理无残留,完成一次能重做的交付 Python后端部署排障18:从空环境到清理无残留,完成一次能重做的交付
最后一次验收不是展示已经运行很久的容器。那样容易遗漏首次初始化、镜像构建、数据卷权限、迁移顺序和证书生成等问题。毕业流程从本次project尚不存在开始,创建独立资源&a… · 2026/11/4 6:54:28
如何打造macOS风格悬浮岛:waybar-themes Island主题实战教程,V7与V1.9a深度解析 【免费下载链接】waybar-themes A collection of Waybar themes inspired by Omarchy, designed to be compatible with various Linux distributions. 项目地址: https://gitcode.com/gh_mirrors/wa/waybar-themes 点击查看 免费下载 想在 Linux 桌面拥有 macOS 同… · 2026/11/4 6:53:28
OpenNHP Agent SDK跨平台集成与C ABI集群化迁移 第一次把 OpenNHP Client Agent SDK 接进自家产品里,我卡住的地方不在业务逻辑,而是在链接器上。头文件翻来覆去就那几个函数,可跨平台编译一跑起来,Linux 上顺得像玩一样,Windows 上直接甩一串 unresolved external&a… · 2026/11/4 9:39:42
原子级控制与超级AI:技术可能性与伦理挑战 1. 原子级控制机器的技术可能性探讨 当我们在科幻作品中看到"控制原子"的概念时,往往会觉得这遥不可及。但事实上,现代纳米技术已经让我们能够在原子尺度上进行操作。1981年,IBM的科学家发明了扫描隧道显微镜(STM),首次… · 2026/11/4 9:39:42
格式工厂:Windows全能多媒体格式转换工具详解 1. 格式工厂:Windows平台的全能格式转换解决方案作为一名长期与多媒体文件打交道的从业者,我深知格式转换是数字内容处理中最基础却最频繁的需求。格式工厂(FormatFactory)这款免费工具在Windows平台上已经服务了用户十余年&#… · 2026/11/4 9:39:42
MCP协议:打破AI工具数据孤岛的轻量级通信方案 1. 项目背景与核心价值 在AI工具生态快速发展的当下,不同系统间的"数据孤岛"问题日益凸显。我们团队在开发智能写作助手OpenClaw时,发现其与客户已有的CRM系统、知识库工具之间存在严重的数据流通障碍——每次都需要手动导出/导入CSV文件&… · 2026/11/4 9:39:42
OpenClaw在Windows平台的部署与优化指南 1. OpenClaw Windows部署全流程解析 作为一款新兴的AIGC开发工具,OpenClaw在Windows平台的部署过程需要特别注意环境配置和版本兼容性问题。本文将基于v2026.1.30版本,详细拆解从零开始的完整安装流程,包含多个关键环节的深度技术解析。 重要… · 2026/11/4 9:39:42
新型储能三层市场结构与BMS/PCS/EMS系统集成解析 简介:《2024中国新型储能行业发展白皮书》由储能领跑者联盟编写,面向储能企业、投资机构、政策研究者及产业链从业者,系统梳理碳中和背景下新型储能的机遇与挑战。资源包共1个PDF文件,约4.52MB,内容涵盖版权与免责声明… · 2026/11/4 9:38:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38