Skip to content

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 流程 ​

text
客户端 ──(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 三段结构 ​

text
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 数据层授权:最容易漏的一层 ​

text
❌ 只校验"登录了"                    → 任意 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 够吗?不够的话怎么加属性?画出最小改动。


相关文档 ​

本区 ​

其他域 ​