02 · 认证授权体系:OAuth2 / JWT / 权限模型
C03 讲「怎么选、怎么落地」,本篇补原理框架:OAuth2 的四个角色与授权流程、JWT 的签名与攻击面、权限模型从 ACL 到 ABAC 的演进逻辑。读完你能读懂任何一份 OAuth2 流程图,而不是只会套库。
速览卡
| 项 | 内容 |
|---|---|
| 一句话定位 | 认证授权体系的原理与判断框架:流程怎么走、签名怎么验、权限怎么建模 |
| 核心内容 | OAuth2 四角色与授权码流程、JWT 签名算法与密钥混淆、ACL/RBAC/ABAC、OIDC 与 SSO |
| 读完能回答 | Authorization Code 为什么比 Implicit 安全?alg=none 是什么坑?RBAC 什么时候不够用? |
| 相关板块 | C03 认证与授权 | N06 Cookie/Session/Token |
工程决策(Session vs JWT、token 放哪、要不要上 OAuth2)看 C03;本篇只讲原理与攻击面。
一、认证与授权:两条独立的时间线
| 认证 Authentication | 授权 Authorization | |
|---|---|---|
| 回答 | 你是谁 | 你能做什么 |
| HTTP 语义 | 401 Unauthorized(其实是"未认证") | 403 Forbidden |
| 时间线 | 登录时发生一次 | 每次访问都要判断 |
| 出错后果 | 放进来一个冒名者 | 放进来一个越权者 |
关键认知:认证只发生一次,授权发生在每一次请求。把权限校验只放在"登录时"是常见的越权根源。
二、OAuth2:四个角色的舞台
| 角色 | 是谁 | 前端类比 |
|---|---|---|
| 资源所有者 Resource Owner | 真正的用户 | 就是你 |
| 资源服务器 Resource Server | 存数据、校验 token 的 API | 后端 API |
| 授权服务器 Authorization Server | 发 token、管 scope | 登录/授权服务 |
| 客户端 Client | 想代表用户访问数据的应用 | 你的 App / 网站 |
OAuth2 的本质不是登录,是"授权第三方访问资源"。客户端拿到的是受限的委托凭证(access token),而不是用户的密码。
2.1 授权码流程:目前唯一推荐的 Web 流程
客户端 ──(1) 跳转授权服务器,带 client_id + redirect_uri + scope──> 授权服务器
用户 ──(2) 在授权服务器上登录并同意──> 授权服务器
授权服务器 ──(3) 302 回 redirect_uri,带一次性 authorization code──> 客户端
客户端 ──(4) 用 code + client_secret 直接换 access token──> 授权服务器(后端到后端,不经过浏览器)为什么不用 Implicit(直接回传 token):
| 授权码模式 | Implicit 模式 | |
|---|---|---|
| 浏览器里出现 token 吗 | 只有一次性 code | 会,token 直接进 URL/页面 |
| token 能被日志/历史/第三方脚本偷吗 | 基本不能 | 能 |
| 现状 | 推荐(SPA 用授权码 + PKCE) | 已废弃 |
PKCE(Proof Key for Code Exchange):SPA/移动端拿不到安全的
client_secret,用「先发的code_verifier派生 challenge」证明"换 code 的就是刚才发起的那个客户端"。用授权码就必须配 PKCE。
三、JWT:签名与攻击面
3.1 三段结构
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . payload(base64) . 签名
Header(算法声明) 载荷(明文可见) 证明"没被改过"- Payload 只是 Base64,不是加密——任何拿到 token 的人都能读内容。
- 签名保证完整性(没被篡改),不保证机密性(内容可见)。
3.2 三个经典签名坑
| 坑 | 现象 | 防御 |
|---|---|---|
alg=none | 攻击者把 Header 改成 alg:none,去掉签名,篡改 payload | 白名单只允许固定算法,none 直接拒绝 |
| 算法混淆 | 用 HS256(对称)的密钥去验 RS256(非对称)的签名 | 校验 kid/alg 必须与信任列表匹配 |
| 弱密钥 | HS256 用 secret/password 当密钥 | 密钥至少 256 位随机,绝不硬编码进前端代码 |
3.3 签发方与验签方
| 算法族 | 用哪把钥匙签 | 用哪把钥匙验 | 场景 |
|---|---|---|---|
| HS256(对称) | 同一个密钥 | 同一个密钥 | 自己签自己验(单一信任方) |
| RS256(非对称) | 私钥 | 公钥 | 多方验签(第三方登录,首选) |
用第三方 OIDC 时,验签用公钥(从 JWKS 端点取),绝不在自己代码里放对方的私钥。
四、权限模型:从简单到复杂的演进
| 模型 | 表达 | 优点 | 何时不够用 |
|---|---|---|---|
| ACL | 用户 → 资源 → 权限(直接授予) | 简单直观 | 用户多了改不动 |
| RBAC | 用户 → 角色 → 权限 | 好管理,绝大多数系统的默认 | "同一角色不同部门权限不同" |
| ABAC | 基于属性(部门/时间/金额/风险)动态判断 | 表达力最强 | 复杂、要策略引擎、难审计 |
演进建议:从 RBAC 起步,遇到真实需求再局部引入 ABAC,不要一上来做通用权限引擎。 判断信号:权限是否依赖请求上下文(谁、什么资源、什么时间、什么金额)而不是只有"角色"这个标签。
4.1 数据层授权:最容易漏的一层
❌ 只校验"登录了" → 任意 user_id 都能查
❌ 只在接口层校验角色 → 内部调用、批量接口绕过
✅ 查询时带归属条件(where id=? and owner_id=?) → 越权从 SQL 层面被拦住IDOR(越权访问)是授权模型设计好后仍最常见的洞:/orders/123 换成 124 就能看别人的。防御不是"查完再比对",而是查询条件就带上归属。
五、OIDC 与 SSO:在授权之上加身份
| 名词 | 关系 | 提供什么 |
|---|---|---|
| OAuth2 | 授权框架 | access token(能访问什么) |
| OIDC | 在 OAuth2 上加身份层 | id_token(你是谁)+ 标准 userinfo |
| SSO | 一次登录、多系统通用 | 会话共享 / 跳转免登 |
常见误解:"微信登录 = OAuth2"其实是 OAuth2 + OIDC:OAuth2 负责委托授权,OIDC 的
id_token才回答"用户是谁"。
六、攻击面检查:认证授权最容易翻车的地方
| 场景 | 典型漏洞 | 防御 |
|---|---|---|
| 密码 | 明文/弱哈希存储 | bcrypt/argon2 慢哈希,绝不 MD5/SHA1 存密码 |
| 令牌 | 放 localStorage、日志里出现 | HttpOnly Cookie;日志脱敏 |
| 过期 | token 永不过期、退出不失效 | 短 access + refresh;黑名单/版本号(见 C03) |
| 重放 | 拦截到 token 无限复用 | 短有效期;关键操作二次校验 |
| 越权 | 只校验"已登录"不校验"有权限" | RBAC/ABAC + 数据层归属过滤 |
| 第三方 | 回调 redirect_uri 可篡改、code 可重放 | 严格校验 redirect_uri;code 一次性 |
七、自检清单
- [ ] 能说清认证与授权的区别,以及 401/403 的语义
- [ ] 知道 OAuth2 四个角色,能画出授权码流程
- [ ] 知道为什么不用 Implicit,以及 SPA 为什么需要 PKCE
- [ ] 知道 JWT 的 Payload 不是加密,签名只保完整性
- [ ] 知道
alg=none与算法混淆这两个坑 - [ ] 权限模型从 RBAC 起步,遇真实需求再演进到 ABAC
- [ ] 权限在数据层也有归属过滤(防 IDOR)
- [ ] 密码用慢哈希,绝不自己造加密
八、常见误区
| 误区 | 事实 |
|---|---|
| "OAuth2 是登录协议" | 它是授权框架;确认身份靠 OIDC |
| "JWT 更安全" | 安全取决于实现;JWT 的代价是难即时失效 |
| "JWT 内容别人看不到" | Payload 只是 Base64,明文可见 |
| "授权码拿到 token 就安全了" | 不配 PKCE 的授权码流程在 SPA/移动端依然可被截 |
| "角色对了就放行" | 还要在数据层带归属条件,否则 IDOR |
| "权限模型越复杂越好" | 复杂度有代价;从 RBAC 起步,遇真需求再演进 |
| "密码哈希一下就安全" | 要用慢哈希(bcrypt/argon2),防撞库 |
| "只在前端隐藏按钮就算没权限" | 接口必须独立校验 |
九、动手练习
练习 A:读懂一张 OAuth2 时序图
找一张 Authorization Code + PKCE 的时序图(搜索 "OAuth2 PKCE sequence diagram"),对着四个角色和五个步骤,说出每一步谁能看到什么、谁能偷到什么。
练习 B:复现算法混淆
搭一个简单的验签服务(FastAPI + PyJWT),先用 RS256 签、再用 HS256 配公钥当密钥去验,观察"验证通过"如何发生。然后加算法白名单修掉它。
练习 C:给一个多租户系统建模权限
需求:同一个"运营"角色,在 A 租户能改数据、在 B 租户只能看。用 RBAC 够吗?不够的话怎么加属性?画出最小改动。
相关文档
本区
- 01 · Web 应用安全 —— CSRF 的受害者正是"只靠 Cookie 验身份"
- S · 安全计划
其他域
- 03 · 认证与授权 —— Session vs JWT 的工程决策、token 存放
- 16 · 服务端安全纵深 —— 密钥管理与越权的服务端落地
- 08 · 前端安全 —— 前端侧 token 存放与 CSRF
- 06 · Cookie/Session/Token 传输 —— 凭证如何在网络中携带