认证、授权与多租户:登录成功只是第一步

用户把地址里的订单号从 42 改成 43,居然看到了别人订单。这不是登录失效,而是资源授权缺失。后端要分别确认调用者是谁,以及这个调用者是否能对这份数据执行当前操作。

1. 认证和授权

认证建立身份,例如验证会话 Cookie 或访问令牌;授权根据身份、角色、资源归属和上下文决定是否允许操作。RBAC 使用角色表达一组权限,ABAC 根据属性和条件判断,真实系统常把角色和资源归属结合使用。

JWT 是一种令牌格式,不等于授权方案。验证签名之外还要按协议检查签发者、受众、有效期等;Payload 编码不是加密。登出时是否撤销令牌、刷新令牌怎样轮换、权限变化多久生效,都要有明确策略。

2. 在读取数据时纳入归属范围

sql
-- PostgreSQL 参数化查询示意:$1 从验证后的身份上下文取得。 SELECT id, total_minor, status FROM orders WHERE tenant_id = $1 AND id = $2;

tenant ID 若直接来自用户可任意修改的正文,就没有形成可信约束。管理员跨租户操作需要独立权限,不应复用普通用户接口时顺手省略过滤。列表、导出、搜索、附件下载和后台任务同样要带入权限范围,不能只保护详情接口。

多租户意味着多个组织共享某些设施,但逻辑数据边界要隔离。共享表、独立 Schema、独立数据库各有成本与隔离差异。无论哪种,缓存键、消息、日志和后台任务都要保留正确租户归属。

3. SQL 注入和输入校验

查询值使用参数绑定,而不是把字符串拼进 SQL。参数绑定通常不适用于动态表名、列名或排序方向;这些结构性输入应映射到程序内白名单。长度、数量、分页上限和请求体大小还需要业务校验,防止单个请求消耗过量资源。

允许用户提交 URL 的功能,要限制出站目的地和重定向,避免服务端访问不该访问的内部资源。上传内容也要检查,不因扩展名叫 .png 就认定字节一定是图片。文件路径应由服务端生成或严格限制在允许范围。

浏览器自动携带 Cookie 的认证方案需要考虑 CSRF:请求可能从其他站点发起,而浏览器仍携带凭据。SameSite、CSRF Token 和来源检查可以按场景组合。HttpOnly 降低脚本直接读取 Cookie 的风险,但不等于消除所有 XSS 后果。

CORS 控制浏览器脚本能否跨源读取某些响应,不是服务端授权机制。非浏览器客户端不靠它获得访问资格。允许来源、凭据和预检策略应与真实前端域名匹配,不能用宽泛配置代替权限设计。

5. 秘密、审计与最小暴露

密码应使用适当的带盐密码哈希方案,不用可逆加密保存以便“找回原密码”。应用凭据放在受控配置或秘密服务里,分环境、限权限,并准备轮换。日志不要记录完整 Cookie、令牌、密码和敏感请求正文。

审计记录关心谁在何时对什么资源做了什么、结果如何;普通调试日志可以采样,关键审计记录则有不同的保存和完整性要求。访问日志本身也可能包含敏感信息,需限制查看范围和保留期限。

练习与参考

准备 A、B 两个租户:A 的用户用 B 的资源 ID 访问详情、列表过滤、导出和附件下载,均应按统一策略拒绝或隐藏。然后测试缓存命中和消息消费路径,确保没有因为绕过常规 Controller 而漏掉隔离。

用实际业务身份测试行级权限

数据库行级权限可以限制用户能读写哪些行,但要先分清执行查询的数据库角色。PostgreSQL 的超级用户、带 BYPASSRLS 的角色会绕过行级规则;表所有者通常也会绕过。因此用管理账号查不到问题,不能证明普通用户隔离正确。

准备甲、乙两个租户的测试数据,用实际业务身份验证四个动作:甲读自己的记录、甲读乙的记录、甲修改乙的记录、甲插入声称属于乙的记录。分别检查可见性条件和写入条件,不能只测 SELECT。连接池复用时,还要确认身份上下文不会留给下一个请求。

本节的核验依据

2026-10-25 查阅。下面的链接支持本节的机制说明;练习场景和选型建议是本教程的教学设计。