在"20 | 安全:攻击与防御"中,我们构建了纵深防御体系,从网络层到应用层逐层设防。然而,所有防御措施的起点都是一个根本问题:你是谁?你能做什么?
这两个问题分别对应安全领域最核心的两个概念——认证(Authentication) 与授权(Authorization)。认证解决身份确认问题,授权解决权限控制问题。二者看似简单,实则是分布式系统设计中最容易出错的环节之一。OWASP 2021 Top 10 中,"失效的访问控制"高居榜首,"身份识别和认证失败"位列第七。
安全管理的核心是信任管理。认证是建立信任的起点,授权是信任边界的定义。理解认证与授权的设计,就是理解如何在分布式系统中安全地建立和管理信任关系。
| 维度 | 认证(Authentication) | 授权(Authorization) |
|---|---|---|
| 核心问题 | 你是谁? | 你能做什么? |
| 输入 | 身份凭据(密码/Token/证书) | 身份标识 + 操作请求 |
| 输出 | 身份声明(Identity Claim) | 允许/拒绝(Permit/Deny) |
| 失败后果 | 身份无法确认,拒绝进入 | 权限不足,拒绝操作 |
| 实现层次 | 通常集中式 | 通常分布式 |
| 典型协议 | OAuth 2.0、SAML、Kerberos | RBAC、ABAC、ACL |
关键洞察:认证和授权的解耦是架构设计的重要原则。认证通常由专门的身份提供商(IdP, Identity Provider)集中处理,授权则分散在各业务服务中。这种解耦使身份管理可以统一,权限控制可以按业务定制。
认证基于以下三类因素的组合:
单因素认证(如仅密码) 的安全性有限——密码可能被钓鱼、撞库、泄露。多因素认证(MFA) 要求用户提供两种以上不同类型的因素,大幅提高攻击成本。
架构师关注点:MFA 不是可选的增强,而是高价值操作的必要保障。NIST SP 800-63B 已明确建议淘汰短信验证码(SIM Swap 攻击),推荐使用 FIDO2/WebAuthn 等基于公钥密码学的硬件认证。
最简单的认证方式:客户端在请求头中携带 Authorization: Basic <base64(username:password)>。
致命缺陷:Base64 是编码而非加密,凭据可被任何中间人读取。除非在 HTTPS 连接上使用,否则完全不安全。即使使用 HTTPS,基本认证仍存在凭据长期有效、无法细粒度授权等局限。
核心思路:用户认证成功后,服务器颁发一个 Token(令牌),客户端在后续请求中携带 Token 而非密码。
Token 的两种实现:
| 维度 | 不透明 Token(Opaque Token) | JWT(JSON Web Token) |
|---|---|---|
| 格式 | 随机字符串(如 UUID) | 结构化 JSON(Header.Payload.Signature) |
| 验证方式 | 查询数据库/缓存 | 验证签名(无需查库) |
| 状态 | 有状态(需存储) | 无状态(自包含) |
| 撤销 | 直接删除存储记录 | 需额外机制(黑名单/短有效期) |
| 信息量 | 无(仅是引用) | 包含用户标识、角色、过期时间等 |
OAuth 2.0 是业界标准的授权框架,其核心目标是:让用户授权第三方应用访问其在另一服务上的资源,而无需向第三方透露密码。
OAuth 2.0 定义了四种角色:
OAuth 2.0 的四种授权流程(Grant Types):
授权码模式(Authorization Code)的完整流程:
state 参数的必要性:防止 CSRF 攻击。客户端生成随机 state,授权服务器原样返回。客户端验证 state 一致后才使用授权码,防止攻击者伪造授权码注入。
PKCE(Proof Key for Code Exchange)扩展:授权码模式下,如果客户端无法安全存储 client_secret(如移动端/SPA),则使用 PKCE。客户端生成 code_verifier 和其哈希 code_challenge,授权码请求携带 code_challenge,Token 请求携带 code_verifier。授权服务器验证二者对应关系,防止授权码被截获后滥用。
OAuth 2.0 是授权框架而非认证协议。OpenID Connect 在 OAuth 2.0 之上增加了身份层:
id_token(JWT 格式),包含用户身份信息(sub、name、email 等)/userinfo 端点,获取完整用户信息openid scope,触发 OIDC 流程关键区别:OAuth 2.0 的 Access Token 用于访问资源,OIDC 的 ID Token 用于证明身份。用 OAuth 2.0 的 Access Token 当作身份凭证是常见反模式——Access Token 是授权凭据,不包含身份断言。
JWT 由三部分组成,以 . 分隔:Header.Payload.Signature
Header:声明令牌类型和签名算法。
Payload:包含声明(Claims)。标准声明包括:
| 声明 | 全称 | 含义 |
|---|---|---|
| iss | Issuer | 签发者 |
| sub | Subject | 主题(用户标识) |
| aud | Audience | 接收方 |
| exp | Expiration Time | 过期时间 |
| nbf | Not Before | 生效时间 |
| iat | Issued At | 签发时间 |
| jti | JWT ID | 唯一标识(防重放) |
Signature:使用 Header 中声明的算法对 Header.Payload 部分签名。签名确保 Payload 未被篡改。
关键警告:JWT 的 Header 和 Payload 仅经过 Base64Url 编码,不是加密。任何持有 JWT 的人都能读取 Payload 内容。敏感信息(密码、密钥)不应放入 JWT。如需保密,应使用 JWE(JSON Web Encryption)。
JWT 的无状态性是其核心优势(无需查库验证),也是其核心缺陷(无法主动撤销)。一旦签发,在 exp 到期前始终有效。
解决方案:
RBAC 是企业应用中最广泛使用的授权模型。其核心思想:用户不直接关联权限,而是通过角色间接获得权限。
RBAC 的演进层级:
| 层级 | 名称 | 特性 |
|---|---|---|
| RBAC0 | 扁平 RBAC | 用户-角色-权限三者的基本映射 |
| RBAC1 | 层次 RBAC | 角色可继承(如"超级管理员"继承"管理员"的所有权限) |
| RBAC2 | 约束 RBAC | 角色互斥(如"出纳"和"审核"不能同一人)、基数约束(角色人数上限) |
| RBAC3 | 统一 RBAC | RBAC1 + RBAC2 的组合 |
RBAC 的局限:
ABAC 基于主体属性、资源属性、操作属性和环境属性的组合评估访问决策:
ABAC 的优势:
ABAC 的代价:
架构关键决策:
认证应集中由 IdP 处理,避免每个服务重复实现认证逻辑。授权应分散到各业务服务,因为权限判断依赖业务上下文。
Trade-off:集中认证引入了单点依赖——IdP 不可用时所有服务无法认证。解决方案:Token 的无状态验证(JWT)可在 IdP 不可用时继续工作;Refresh Token 的更新走降级路径。
JWT 的无状态性使其天然支持水平扩展,但牺牲了即时撤销能力。Opaque Token 支持即时撤销,但每次验证都需要查询存储。
Trade-off:推荐采用混合策略——短有效期(5-15 分钟)的 JWT Access Token + 长有效期的 Opaque Refresh Token。前者用于高频访问验证(无状态),后者用于低频的令牌刷新(有状态,可撤销)。
RBAC 简单直观,适合权限模型稳定、角色划分清晰的企业应用。ABAC 灵活强大,适合权限策略复杂、动态变化的多租户平台。
Trade-off:RBAC 的角色爆炸问题在大型系统中难以避免,但 ABAC 的策略复杂度也会导致管理和审计困难。实践中常采用混合模型:RBAC 作为基础框架(粗粒度),ABAC 作为补充(细粒度条件约束)。
强制 MFA、频繁重新认证、复杂的密码策略虽然安全,但会降低用户体验。用户可能选择弱密码来应对复杂策略,或在便利贴上写下密码,反而降低安全性。
Trade-off:基于风险的自适应认证(Risk-Based Authentication)——低风险操作(常规浏览)仅需基本认证,高风险操作(大额转账、敏感数据访问)触发 MFA。这种渐进式安全模型在安全性和体验之间取得了更好的平衡。
传统安全模型基于网络边界——内网可信,外网不可信。零信任模型否定这一假设:永不信任,始终验证,无论请求来自内网还是外网。
零信任的核心原则:
Trade-off:零信任架构显著增加了认证/授权的调用频率和复杂度,需要强大的身份基础设施和高性能的策略引擎支撑。
在微服务架构中,OAuth 2.0 的典型实现:
OAuth 2.0 的 Access Token 是授权凭据而非身份断言。Access Token 的格式是不透明的(对资源服务器而言),其内容可能随时变化(如权限范围缩减),不能用于推断用户身份。
正确做法:使用 OpenID Connect 的 ID Token 作为身份凭证,Access Token 仅用于授权。
JWT 的 Payload 仅经过 Base64Url 编码,任何人都能解码查看。将密码、身份证号、银行卡号等敏感信息放入 JWT 是严重的安全隐患。
正确做法:JWT 中仅存储最小必要的身份标识(sub)和元数据(exp、iss 等)。敏感信息通过 API 按需获取,且受授权控制。
多租户系统中的权限设计需要同时解决租户隔离和租户内权限两个层面:
设计要点:
在服务间通信和第三方集成场景中,API Key 是常用的认证方式。API Key 与用户 Token 的关键区别:
| 维度 | API Key | 用户 Token |
|---|---|---|
| 代表 | 服务/应用 | 用户 |
| 范围 | 通常固定 | 可能动态变化 |
| 撤销 | 立即生效 | 受 Token 有效期限制 |
| 审计 | 关联到服务 | 关联到具体用户 |
最佳实践:API Key 应设置最小权限范围、定期轮换、支持独立撤销(每把 Key 独立,撤销一把不影响其他)、记录完整的调用审计日志。
延伸阅读: