认证与授权核心机制 (JWT / OAuth2 / RBAC)
校招考察频度:★ 高频(重点常考·大厂分水岭) 一句话速记:认证解决“你是谁”(Authentication),授权解决“你能做什么”(Authorization);Session 有状态易撤销但难跨机,JWT 无状态自包含签名但难主动吊销;OAuth2 借助临时授权码安全下发令牌;越权防御关键在于强校验当前登录态下的数据归属。
在后端接口设计和安全面试中,“认证与授权”是几乎每个做过 Web 项目的应届生都会被深挖的重灾区。很多初学者经常分不清“登录态保持”、“Token 验签”和“权限拦截”的边界。
一、0 基础生动类比:身份证与门禁卡模型
| 安全概念 | 现实生活场景 | 对应技术实现 |
|---|---|---|
| 认证(Authentication) | 去派出所刷身份证和人脸识别:证明“我确实是张三本尊”。 | 用户输入账号密码/短信验证码,服务端核验无误后建立登录态。 |
| 授权(Authorization) | 拿着公司工牌刷研发大楼门禁:保安系统判断“张三是否有权进入机房”。 | 校验用户角色与权限列表(如 RBAC 模型),判定是否有权访问该 API。 |
| OAuth2 第三方授权 | 业主授权给顺丰外卖小哥一张临时门禁访客码:只允许小哥在今天下午 14:00~15:00 刷卡进单元门,不需要把家里的防盗门钥匙交给外卖员! | 用户通过微信登录第三方网站,微信向第三方下发受限令牌,无需交出微信密码。 |
二、两大主流登录态方案:Cookie-Session vs JWT
Cookie-Session vs JWT 架构对比
1. 深度对比表
| 维度 | Cookie-Session 机制 | JWT(JSON Web Token)机制 |
|---|---|---|
| 状态存储 | 有状态:服务端必须存 Session(单机存内存,多实例必须存 Redis) | 完全无状态:服务端不存任何数据,信息自包含在 Token 中 |
| 横向扩展 | 依赖集中式 Redis 集群,存在单点与网络查询开销 | 极其强悍:跨微服务任意节点仅需本地秘钥解密验签即可,无外部依赖 |
| 主动撤销/登出 | 极其简单:服务端直接在 Redis 中 DEL session_id 瞬间失效 | 极其困难:Token 一旦签发,在有效期内任何持有者都能访问(除非搭建黑名单) |
| 移动端友好度 | 依赖浏览器 Cookie 机制,原生 App/小程序处理繁琐 | 极高:存入 LocalStorage 或内存,通过 HTTP Header Authorization 传递 |
| 数据安全性 | 敏感信息留在服务端,泄露面小 | Payload 仅做 Base64 编码,未加密!绝不可放敏感密码 |
2. 深入拆解 JWT 的三部分与防伪原理
一个合法的 JWT 字符串形如:xxxxx.yyyyy.zzzzz:
- Header(标头):声明加密算法与令牌类型,如
{"alg": "HS256", "typ": "JWT"}。 - Payload(载荷):存放公开的业务声明,如用户 ID、过期时间
exp、角色role。 - Signature(数字签名):防篡改的核心!
Signature = HMAC-SHA256(base64Url(Header) + "." + base64Url(Payload), secret)
- 防伪原理:如果攻击者尝试在前端私自修改 Payload 里的
user_id = 1变成管理员,由于攻击者没有服务端私存的secret秘钥,服务端在校验时根据 Header 和篡改后的 Payload 重新计算签名,必然与携带的 Signature 不符,直接被当场拦截!
三、OAuth2.0 授权码模式(Authorization Code Flow)
OAuth2 是开放授权的行业标准,其中**“授权码模式(Authorization Code)”**安全性最高、应用最广(如“微信扫码登录”、“GitHub 一键登录”)。
┌─────────────────────────────────────────────────────────────┐
│ 1. 用户点击“微信登录”,重定向到微信认证授权页面 │
└──────────────────────────────┬──────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 用户扫码同意授权,微信将浏览器重定向回第三方网站: │
│ https://myapp.com/callback?code=AUTH_CODE (临时授权码) │
└──────────────────────────────┬──────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. 核心安全隔离点: 第三方后端在【服务器内网】拿该 code + 秘钥│
│ 向微信服务器换取真实的 access_token 和用户 openid │
└──────────────────────────────┬──────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 微信校验无误,下发 access_token,第三方完成登录并下发会话 │
└─────────────────────────────────────────────────────────────┘经典面试拷问:为什么中间非要多此一举搞一个临时授权码
code?微信为什么不直接把access_token重定向传回浏览器?
- 答:因为浏览器重定向的 URL 会残留在浏览器历史记录、网络代理日志(HTTP Referer)中,存在极高的前端泄露风险!而有了中间这道临时
code(且设定为 5 分钟内一次性失效),第三方网站是用自己的后端服务器在受保护的内网链路中拿着自己的app_secret向微信换取 token,真实的令牌绝不暴露给前端浏览器,安全性坚如磐石!
四、授权模型与越权漏洞(OWASP Top 1)
1. RBAC(基于角色的访问控制,Role-Based Access Control)
大厂后台系统的标准模型:用户(User) ➔ 角色(Role) ➔ 权限(Permission)。
- 用户不直接绑定权限,而是被赋予一个或多个角色(如:
客服人员、运营经理、超级管理员)。 - 角色对应一组具体的原子操作权限(如:
order:read,order:export,order:delete)。
2. 越权漏洞防御(面试高频必杀)
| 越权类型 | 漏洞场景复现 | 防护核心原则 |
|---|---|---|
| 水平越权(Horizontal) | 张三登录后,抓包把 URL 改为 /api/order/1002(李四的订单号),居然直接看到了李四的私人收件地址! | 数据归属强制绑定校验!查询时必须从当前线程安全上下文中提取当前登录用户的 current_user_id:SELECT * FROM orders WHERE order_id = ? AND user_id = current_user_id |
| 垂直越权(Vertical) | 普通注册用户张三,直接通过 Postman 猜测并调用管理员接口 /api/admin/delete_user,居然执行成功! | 接口级严格拦截!在网关或 Spring Security / FastAPI 依赖注入层,通过拦截器对每个受保护接口执行角色或权限硬校验。 |
五、知识脉络关联网(Knowledge Highway)
- ⬅️ 底层协议基础:Cookie 是如何在 HTTP 报文中被传输与隔离的?参见 HTTP 协议与状态码精讲。
- ➡️ FastAPI 生产落地:如何使用 Python FastAPI 优雅编写 OAuth2 与 JWT 校验?参见 FastAPI 鉴权与安全实战。
- 🔄 Web 安全全景:除了越权,XSS 和 CSRF 怎么防?参见 后端安全:注入、XSS 与 CSRF 防护。
六、校招高频面试追问
- JWT 怎么实现主动注销(登出)或修改密码后旧 Token 失效?
- 方案一:Redis 白名单 / 黑名单机制。用户点击登出后,将该 JWT 的唯一标识
jti写入 Redis 黑名单,过期时间设置为该 Token 的剩余有效时长。网关解密后先查 Redis,命中黑名单则拒绝; - 方案二:双 Token 机制(Access Token + Refresh Token)。核心访问 Token 只给 5~15 分钟超短有效期,刷新 Token 存入数据库或 Redis(长达 7 天)。用户登出直接在服务端删除刷新 Token,短效 Token 自然在几分钟内失效;
- 方案三:用户表维护
token_version或last_pwd_change_time。Token 的 Payload 携带当时的版本号。修改密码后用户版本号自增,服务端验签后比对版本号不符立即拒绝。
- 方案一:Redis 白名单 / 黑名单机制。用户点击登出后,将该 JWT 的唯一标识
- 什么是 JWT 的算法混淆漏洞(
alg=none)?- 早期部分第三方库在解析 JWT 时,如果攻击者恶意将 Header 中的算法字段改为
"alg": "none",并清空 Signature 签名,该库会误以为这是一个未加密的调试令牌而直接放行,从而伪造任意用户身份!防御方案是:在服务端代码中强制显式锁定只接受特定算法(如HS256或RS256),严禁使用 Header 中动态声明的算法。
- 早期部分第三方库在解析 JWT 时,如果攻击者恶意将 Header 中的算法字段改为