微服务中的身份验证和授权
需求
- 设计一个从简单开始但可以随着业务扩展的身份验证解决方案
- 考虑安全性和用户体验
- 讨论该领域的未来趋势
大局观:身份验证(AuthN)、授权(AuthZ)和身份管理
首先,回归基础
- 身份验证:确定你是谁
- 授权:确定你可以做什么
在开始时……让我们有一个简单的服务……

- 分层架构
客户端存储一个cookie或token作为登录状态的证明。(代客钥匙模式)
- 服务器持久化相应的会话
token通常采用JWT格式,由从安全位置(环境变量、AWS KMS、HashiCorp Vault等)获取的密钥签名
- 流行的Web框架通常提供开箱即用的身份验证解决方案
然后,随着业务的增长,我们用AKF规模立方体来扩展系统:
- X轴:水平克隆
- Y轴:功能分解
- Z轴:分片
加上康威定律:组织设计的系统反映其沟通结构。我们通常将架构演变为微服务(参见为什么选择微服务?了解更多)
- 顺便说一句,"微服务与单体"和"多仓库与单仓库"是不同的概念。
- 对于企业,有员工身份验证和客户身份验证。我们更关注客户身份验证。
在微服务世界中,让我们抽取身份验证和授权服务的功能切片,并有一个身份和访问管理(IAM)团队在进行相关工作。

- 身份感知代理 是一个反向代理,允许公共端点或检查受保护端点的凭证。如果凭证未提供但被要求,则将用户重定向到身份提供者。例如:k8s ingress controller、nginx、envoy、Pomerium、ory.sh/oathkeeper等。
- 身份提供者和管理者 通过登录、忘记密码、MFA注册等工作流管理用户身份。例如:ory.sh/kratos、keycloak
- OAuth2和OpenID Connect提供者 为第一方登录(OIDC)签发令牌,并允许第三方开发者通过OAuth2接入。实践中通常与身份提供者合并在同一服务中——Keycloak、Auth0和Okta均同时具备两者——但也可分离:例如使用Ory Kratos管理身份 + Ory Hydra处理OAuth2/OIDC。
- 授权 服务控制谁可以做什么。
身份验证
1. 身份提供者
- 最简单的解决方案是提交用户的身份证明并发放服务凭证。
- 密码哈希推荐使用 Argon2id(OWASP当前首选)、bcrypt 或 scrypt
- 然而,现代应用程序通常处理复杂的工作流,如条件注册、多步骤登录、忘记密码等。 这些工作流本质上是状态机中的状态转换图。
工作流:用户设置和个人资料更新
Ory.sh/Kratos作为示例架构

2. 第三方OAuth2
OAuth2让用户或客户端经历几种授权类型(不确定使用哪个?请查看这个),如
- 授权码 + PKCE 授权 — Web(SPA)和移动/原生应用
- 客户端凭证授权 — 后端/机器间(M2M)服务
- 设备授权授权 — 输入受限的设备(智能电视、CLI工具、物联网)
然后最终获得访问令牌和刷新令牌
- 访问令牌是短期有效的,因此如果被泄露,攻击窗口很短
- 刷新令牌一次性使用并在每次使用后轮换;机密客户端(服务端)将其与客户端密钥配对,而公共客户端(SPA、移动端)依赖轮换刷新令牌——存储在 httpOnly cookie 中,切勿存入 localStorage
假设在这个工作流中涉及许多实体——客户端、资源拥有者、授权服务器、资源服务器、网络等。更多的实体会增加被攻击的暴露面。一个全面的协议应该考虑所有边缘情况。例如,如果网络不是 HTTPS / 不能完全信任怎么办?
OpenID Connect 是构建在 OAuth2 之上的身份协议,它添加了携带用户身份声明的签名 ID 令牌(JWT),从而实现跨服务的单点登录(SSO)。
在这些工作流和令牌处理过程中有很多棘手的细节。不要重新发明轮子。
3. 服务间身份验证
在微服务中,服务之间同样需要互相验证身份——这与面向用户的身份验证不同,但经常被忽视:
- mTLS(双向TLS) — 双方均出示证书;服务网格(Istio、Linkerd)通过 sidecar 透明地处理此过程。身份绑定到工作负载,而非人类用户。
- SPIFFE/SPIRE — 工作负载身份标准。每个服务获得一个加密的 SVID(SPIFFE 可验证身份文档),可与 mTLS 或 JWT-SVID 配合使用。
- JWT传播 — 将原始用户 JWT 转发到下游服务,或由网关为下游服务签发新的服务令牌,从而无需每个服务重新验证用户凭证即可完成授权。
不要在每个服务中各自实现令牌验证——使用共享库或 sidecar 统一处理。
4. 多因素身份验证
问题:凭证填充攻击
用户倾向于在多个网站上重复使用相同的用户名和密码。当其中一个网站遭遇数据泄露时,黑客会使用这些泄露的凭证对其他网站进行暴力攻击。
- 多因素身份验证:短信、电子邮件、电话语音一次性密码、身份验证器 TOTP
- 速率限制器、失败禁止和异常检测
挑战:电子邮件或短信的送达率差
- 不要将营销电子邮件渠道与事务性渠道共享。
- 语音一次性密码通常具有更好的送达率。
5. 无密码
-
通行密钥(WebAuthn/FIDO2) — 当前防钓鱼无密码身份验证的标准。所有主流平台(Apple、Google、Microsoft)和浏览器均原生支持。凭证是绑定到设备和源域名的私钥,从结构上杜绝了钓鱼攻击。推荐所有新实现优先采用。
- 同步通行密钥(iCloud 钥匙串、Google 密码管理器)支持跨设备使用
- 硬件安全密钥(YubiKey)适用于高保障场景
-
生物识别:指纹、面部识别——通常通过底层使用 WebAuthn 的平台身份验证器实现。权衡依然存在。
-
推送通知——被 Duo、Okta Verify 等应用采用
客户端如何订阅服务器的状态?短轮询、长轮询、WebSocket 或服务器推送事件。
6. 市场上的供应商
不要重新发明轮子。
- 托管解决方案:Auth0、Okta、Amazon Cognito、Firebase Authentication、Clerk、WorkOS、Stytch。Clerk 和 WorkOS 已成为新创业公司的首选——Clerk 以开发者体验见长,WorkOS 专注 B2B/企业 SSO 功能;Auth0/Okta 在大型企业合规需求方面依然强势。
- 本地/自托管:ory.sh、Keycloak 、SuperTokens。
7. 优化
挑战1:Web登录非常慢或根本无法提交登录表单。
- JS包对于移动Web来说太大
- 构建一个轻量级的PWA版本的SPA(单页面Web应用)。无论如何使包小——例如,preact或inferno
- 或者根本不使用SPA。简单的MPA(多页面Web应用)与原始HTML表单提交效果很好
- 浏览器兼容性
- 使用BrowserStack或其他工具在不同浏览器上进行测试
- 数据中心距离太远
- 将静态资源放到边缘/CDN,并通过Google骨干网中继API请求
- 建立一个本地数据中心 😄
挑战2:账户接管
挑战3:账户创建耗时过长
当后端系统变得过于庞大时,用户创建可能会分散到许多服务,并在不同数据源中创建许多条目。在注册结束时等待15秒感觉很糟糕,对吧?
- 收集并逐步注册
- 异步