在现代 Web 应用和企业级系统中,认证(Authentication)是一个绕不开的话题。无论是普通的网站登录、移动应用授权,还是企业内部的统一身份认证平台,都需要解决一个核心问题:如何确认用户是谁,以及如何让不同系统安全地共享身份信息?
在日常开发中,我们经常会遇到 Session、Cookie、JWT、OAuth 2.0、OpenID Connect(OIDC)、SAML、CAS、LDAP 等技术。它们看起来都与登录有关,但实际上解决的问题并不完全相同。
例如,JWT 是一种令牌格式,OAuth 2.0 是授权框架,OIDC 在 OAuth 2.0 之上增加了身份认证能力,SAML 是一种基于 XML 的身份联合标准,而 CAS 是一种常见的单点登录协议。
如果不区分它们的职责,就很容易出现一些误解,例如把 OAuth 2.0 直接当作登录协议,或者认为使用了 JWT 就天然具备安全认证能力。
本文将从基础概念开始,逐步介绍这些技术的工作原理、适用场景和实际区别。
一、认证、授权与身份管理
首先需要明确三个概念:认证、授权和身份管理。
认证(Authentication) 解决的是“你是谁”。
用户输入用户名和密码、通过短信验证码登录,或者使用企业统一身份认证平台完成登录,都是认证过程。系统需要验证用户提供的凭证是否可信,并建立经过验证的身份。
授权(Authorization) 解决的是“你可以做什么”。
用户登录之后,系统还需要判断其是否有权访问某个接口、查看某条数据,或者执行某项操作。例如,普通用户只能查看自己的订单,管理员可以管理所有订单。
身份管理(Identity Management) 则关注用户身份的整个生命周期,包括用户创建、组织关系、账号禁用、角色分配、密码策略、身份同步和离职账号回收等。
这三个概念相互关联,但不能混为一谈。
| 概念 | 核心问题 | 常见技术或机制 |
|---|---|---|
| 认证 | 用户是谁? | 密码、短信验证码、MFA、OIDC |
| 授权 | 用户能做什么? | RBAC、ABAC、OAuth 2.0、权限策略 |
| 身份管理 | 身份如何创建、维护和同步? | LDAP、SCIM、目录服务、身份管理平台 |
| 会话管理 | 如何维持登录状态? | Cookie、Session、访问令牌、刷新令牌 |
| 单点登录 | 如何让多个系统共享登录状态? | OIDC、SAML、CAS |
这里有一个重要区别:OAuth 2.0 主要解决授权问题,而 OIDC 才是在 OAuth 2.0 基础上提供标准化身份认证能力的协议。
二、传统认证机制:Cookie、Session 与 JWT
在讨论 OAuth 2.0 和 OIDC 之前,先理解传统 Web 应用如何维护登录状态。
1. Cookie 与 Session
早期 Web 应用通常采用服务端 Session 机制。
用户登录成功后,服务端创建一个 Session,并生成对应的 Session ID,再通过 Cookie 将这个 ID 返回给浏览器。
浏览器 服务端
| |
|------ POST /login -------->|
| | 验证用户名和密码
| | 创建 Session
|<----- Set-Cookie ----------|
| |
|------ GET /profile -------->|
| Cookie: session_id=xxx |
| | 根据 Session ID 查找会话
|<----- 用户信息 -------------|
浏览器通常只需要保存 Session ID,真正的会话数据保存在服务端内存、Redis 或其他存储中。
优点:
- 服务端可以主动删除会话,实现登出和强制下线。
- 敏感的会话状态不需要全部暴露给客户端。
- 权限和会话状态可以由服务端统一控制。
缺点:
- 多实例部署时需要共享 Session,通常需要 Redis 等存储。
- 需要处理会话过期、续期和并发访问。
- 跨域和跨站场景下需要正确配置 Cookie 属性。
例如,NestJS 应用部署了多个实例,如果每个实例都只保存自己的内存 Session,那么请求切换到另一个实例后可能无法识别原来的会话。共享 Session 存储可以解决这个问题。
2. JWT
JWT(JSON Web Token)是一种令牌格式,常用于在客户端和服务端之间传递声明信息。
典型的 JWT 由三部分组成:
Header.Payload.Signature
例如:
{
"sub": "user_123",
"iss": "https://auth.example.com",
"aud": "api.example.com",
"exp": 1790000000,
"scope": "profile:read"
}
上面的 JSON 只是 Payload 的示意内容,实际 JWT 会对 Header 和 Payload 进行 Base64URL 编码,并根据指定算法生成签名。
需要注意:JWT 通常只是经过编码和签名,并不意味着内容经过加密。 因此,不应该把密码、密钥等敏感数据直接放进 JWT Payload。
JWT 的优点是服务端可以在验证签名和声明后,无需每次查询会话存储即可完成基础校验,适合分布式 API 场景。
但它也存在一些问题:
- 令牌一旦签发,在过期之前通常仍然有效,除非额外设计撤销机制。
- 权限发生变化后,旧令牌中的权限声明可能仍然存在。
- 令牌泄露后,攻击者可能在有效期内冒用令牌。
- 令牌过大时,会增加请求头和网络传输开销。
实际使用时,服务端至少应根据令牌类型和协议要求校验签名、允许的算法、签发者 iss、接收方 aud、过期时间 exp 等声明。不能只解码 JWT,就认为用户身份可信。
JWT 也不是 OAuth 2.0 的必选项。OAuth 2.0 的访问令牌既可以采用 JWT,也可以采用不透明字符串。
3. Session 与 JWT 如何选择?
| 对比项 | Session | JWT |
|---|---|---|
| 状态存储 | 通常保存在服务端 | 常见设计是令牌携带声明 |
| 请求验证 | 查询或验证会话状态 | 验证签名及相关声明,或调用令牌校验服务 |
| 主动撤销 | 通常比较直接 | 通常需要额外的撤销或状态检查机制 |
| 多实例部署 | 通常需要共享会话存储 | 可以减少每次请求对会话存储的依赖 |
| 适用场景 | 传统 Web 应用、后台管理系统 | API、分布式服务、令牌交换场景 |
不要简单地认为 JWT 一定优于 Session。对于普通后台管理系统,使用安全配置的 Cookie 加 Session,可能比自己设计一整套 JWT 刷新、撤销和轮换机制更简单可靠。
三、OAuth 2.0:委托授权的标准框架
OAuth 2.0 是现代身份与访问管理体系中的核心协议之一。
它主要解决的问题是:用户能否授权一个客户端,代表自己访问某些受保护的资源,而不必把自己的密码交给这个客户端?
例如,一个日历应用希望读取用户在另一个平台上的日历数据。用户可以在资源平台完成授权,日历应用获得有限范围的访问令牌,而不需要知道用户的登录密码。
OAuth 2.0 中的主要角色包括:
| 角色 | 含义 |
|---|---|
| Resource Owner | 资源所有者,通常是用户 |
| Client | 请求访问资源的应用 |
| Authorization Server | 授权服务器,负责授权流程和令牌签发 |
| Resource Server | 资源服务器,提供受保护的 API |
典型流程如下:
用户 客户端 授权服务器 资源服务器
| | | |
|-- 访问应用 ---->| | |
| |-- 发起授权 ------->| |
|<---------------- 登录和授权页面 -----| |
|-- 完成授权 ------------------------->| |
| |<-- 授权码 ----------| |
| |-- 交换令牌 -------->| |
| |<-- Access Token ----| |
| |---------------------------------------->|
| | 携带 Access Token |
| |<----------------------------------------|
实际流程通常会通过浏览器重定向完成授权码传递,并由客户端在符合协议要求的情况下交换令牌。上图省略了部分请求参数和安全校验。
1. Authorization Code Flow
授权码模式是当前非常重要的 OAuth 2.0 授权流程。
其基本步骤如下:
- 客户端将用户重定向到授权服务器。
- 用户在授权服务器完成身份验证,并同意授权。
- 授权服务器将用户浏览器重定向回客户端,并附带授权码。
- 客户端使用授权码向令牌端点申请访问令牌。
- 客户端使用访问令牌调用受保护的 API。
对于公开客户端,例如原生移动应用和浏览器中的纯前端应用,应使用 PKCE 来降低授权码被截获后遭到滥用的风险。
授权请求通常包含以下参数:
response_type=code
client_id=web-client
redirect_uri=https://app.example.com/callback
scope=profile:read
state=random_value
code_challenge=...
code_challenge_method=S256
其中:
client_id:客户端标识。redirect_uri:授权完成后的回调地址,必须符合服务端注册的回调地址策略。scope:请求的授权范围。state:用于关联请求并防范授权响应被伪造或串用。code_challenge:PKCE 机制中的挑战值。
对于 PKCE,客户端生成随机的 code_verifier,再根据规定的算法计算 code_challenge。交换授权码时,客户端需要提交对应的 code_verifier。
2. 为什么不推荐密码模式?
OAuth 2.0 早期定义过 Resource Owner Password Credentials Grant,也就是密码模式。
这种模式要求客户端直接收集用户的用户名和密码,再将凭证提交给授权服务器。
它存在明显的问题:
- 用户必须把密码交给客户端。
- 客户端更容易接触并泄露用户凭证。
- 无法充分利用授权服务器的交互式登录能力,例如统一登录、MFA 和外部身份提供商。
- 很难适应现代浏览器和移动端应用的安全要求。
现代 OAuth 2.0 安全实践不推荐使用密码模式。新系统通常应采用授权码模式,并根据客户端类型使用 PKCE。
3. Access Token 与 Refresh Token
OAuth 2.0 中最常见的两种令牌是 Access Token 和 Refresh Token。
Access Token 用于访问受保护资源。资源服务器根据令牌的类型、有效性、受众和授权范围判断是否允许请求。
Refresh Token 用于在符合授权服务器策略的情况下申请新的访问令牌,而不需要用户每次重新完成登录。
二者的用途不同,不应该混用:
- Access Token 应限制有效期和访问范围。
- Refresh Token 通常需要更严格的保护。
- 刷新令牌可以采用轮换机制,并在发现旧令牌被重复使用时撤销相关令牌族。
- 令牌的保存位置需要结合客户端类型和威胁模型设计。
对于服务端 Web 应用,可以将令牌保存在服务端会话中,让浏览器只持有安全的会话 Cookie。对于纯前端应用,应认真评估令牌在浏览器中的存储方式以及 XSS 风险。
4. OAuth 2.0 不等于用户登录
这是理解 OIDC 最关键的一点。
假设一个客户端获得了以下令牌:
{
"access_token": "opaque-or-jwt-token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "calendar:read"
}
这个令牌可能仅仅表示客户端被允许读取日历数据。它并不一定包含用户身份,更不代表客户端可以仅凭这个令牌判断当前登录用户是谁。
OAuth 2.0 本身定义的是授权框架,而不是标准化的用户身份认证协议。
如果应用需要使用外部身份提供商完成登录,并可靠地获得经过验证的用户身份信息,就需要考虑 OpenID Connect。
四、OIDC:建立在 OAuth 2.0 之上的身份认证协议
OIDC(OpenID Connect)是一种身份认证协议,构建于 OAuth 2.0 之上。
它扩展了 OAuth 2.0 的能力,使客户端不仅可以获得访问资源的授权,还可以验证用户身份,并获取标准化的身份声明。
常见场景包括:
- 使用企业统一身份平台登录内部系统。
- 使用第三方账号登录网站。
- 移动应用通过统一身份平台完成登录。
- 多个业务系统共享同一个身份提供商。
1. OIDC 的核心概念
OIDC 在 OAuth 2.0 的基础上引入了几个重要概念。
Identity Provider(IdP):身份提供商,负责认证用户并提供身份信息。
Relying Party(RP):依赖方,即依赖身份提供商完成用户认证的客户端应用。
ID Token:身份令牌,通常是包含身份声明的 JWT,用于向客户端传递经过认证的用户身份信息。
UserInfo Endpoint:用户信息端点,客户端可以使用符合要求的 Access Token 获取额外的用户声明。
OIDC 中的 scope 通常可以包含 openid,这也是区分普通 OAuth 2.0 请求和 OIDC 认证请求的重要标志。
例如:
scope=openid profile email
其中:
openid:表示发起 OIDC 身份认证请求。profile:请求常见的个人资料声明。email:请求电子邮箱相关声明。
实际返回哪些字段,仍取决于身份提供商的支持情况、用户授权和隐私策略。
2. ID Token 与 Access Token 的区别
这两个令牌非常容易被混淆。
| 对比项 | ID Token | Access Token |
|---|---|---|
| 主要用途 | 向客户端表达用户身份认证结果 | 访问受保护资源 |
| 接收方 | OIDC 客户端 | 资源服务器 |
| 常见内容 | 用户标识、签发者、接收方、认证时间等声明 | 由授权服务器定义,可以是不透明字符串或 JWT |
| 校验重点 | 签名、iss、aud、exp、nonce 等 | 根据令牌类型和资源服务器策略进行校验 |
| 能否直接用于业务 API | 通常不应作为 API 访问凭证 | 在符合 API 要求时可以 |
例如,ID Token 可能包含:
{
"iss": "https://auth.example.com",
"sub": "user_123",
"aud": "web-client",
"exp": 1790000000,
"iat": 1789996400,
"nonce": "request_nonce"
}
其中:
iss:令牌签发者。sub:在对应签发者范围内稳定标识用户的主体标识。aud:令牌的接收方,通常包括对应的客户端 ID。exp:过期时间。iat:签发时间。nonce:用于将身份认证响应与客户端发起的认证请求关联起来。
OIDC 客户端不能仅仅解码 ID Token 就信任其中的内容,而应按照协议要求校验签名、签发者、受众、有效期,以及适用场景下的 nonce 等参数。
还要注意,sub 不一定是用户的邮箱或用户名。它通常才是更适合用于关联外部身份的主体标识。
3. OIDC Authorization Code Flow
OIDC 常见的登录流程同样采用授权码模式:
浏览器 业务应用 身份提供商 业务后端
| | | |
|-- 访问应用 --->| | |
| |-- 跳转登录 ----->| |
|<--------------- 登录页面 ---------| |
|-- 完成认证 ---------------------->| |
|<--------------- 返回授权码 -------| |
|-- 携带授权码回调业务应用 ----------------------------->|
| |<-- 服务端换取令牌 --|
| |--- 返回令牌 ------->|
| |<-- 建立本地登录会话 ------------------|
这里展示的是一种常见的服务端 Web 应用架构。实际部署中,授权码可能先返回浏览器,再由后端处理回调;令牌交换发生在客户端或后端,取决于应用架构和客户端类型。
需要特别强调:
- 使用
state将授权响应与原始请求关联。 - 使用
nonce将 ID Token 与认证请求关联。 - 对公开客户端使用 PKCE。
- 严格校验回调地址。
- 校验 ID Token 的签名和声明。
- 不要将 ID Token 当作通用 API 访问令牌。
4. OIDC Discovery 与 JWKS
OIDC 还提供了标准化的服务发现机制。
很多身份提供商会公开一个 Discovery 文档,例如:
https://auth.example.com/.well-known/openid-configuration
该文档通常包含:
issuer:身份提供商标识。authorization_endpoint:授权端点。token_endpoint:令牌端点。userinfo_endpoint:用户信息端点。jwks_uri:获取公钥集合的地址。- 支持的认证方式、签名算法和其他协议能力。
客户端可以通过 jwks_uri 获取公钥集合,验证 ID Token 的签名。
生产环境不应无条件信任令牌头部提供的任意算法或密钥地址。客户端应基于可信的 Issuer 配置和服务发现结果验证令牌,并处理密钥轮换。
5. OAuth 2.0 与 OIDC 的关系
可以简单理解为:
OAuth 2.0
├── 授权框架
├── Access Token
├── Refresh Token
└── 授权服务器与资源服务器交互
OIDC
├── 建立在 OAuth 2.0 之上
├── 增加身份认证语义
├── ID Token
├── UserInfo Endpoint
└── Discovery、JWKS 等标准机制
如果你的目标是让用户通过统一身份平台登录业务系统,通常优先考虑 OIDC,而不是只使用 OAuth 2.0。
五、SAML:企业级身份联合认证标准
SAML(Security Assertion Markup Language)是一种基于 XML 的开放标准,用于在不同安全域之间交换认证和授权相关声明。
在企业环境中,SAML 尤其常见于传统 SaaS 应用、企业门户、办公系统和组织内部的单点登录。
例如,一家公司已经有统一的企业身份平台,希望员工登录企业门户后,就能访问 CRM、OA、财务系统等业务应用。
如果这些应用支持 SAML,企业就可以让统一身份平台负责员工认证,再将认证结果安全地传递给各个业务系统。
1. SAML 的核心角色
SAML 常见的两个角色是:
| 角色 | 含义 |
|---|---|
| Identity Provider(IdP) | 身份提供商,负责认证用户并生成 SAML Response |
| Service Provider(SP) | 服务提供商,即接收认证结果并建立本地登录状态的业务应用 |
这里的 SP 可以理解为业务系统,IdP 则负责统一登录。
2. SAML Web SSO 流程
SAML Web 浏览器单点登录常见的方式是 SP 发起登录。
用户浏览器 SP 业务系统 IdP 身份平台
| | |
|---- 访问业务系统 ---->| |
|<--- 登录跳转 ---------| |
|---------------- 发起 SAML 请求 --------------->|
|<---------------- 登录页面 ---------------------|
|---------------- 提交登录凭证 ----------------->|
|<--------------- SAML Response -----------------|
|---- 向 SP 提交 SAML Response ----------------->|
| | 验证响应和断言 |
|<---------------- 建立本地会话 ------------------|
实际部署中,浏览器通常会通过 HTTP Redirect 或 HTTP POST 绑定传递协议消息。
SAML Response 中可能包含一个或多个 Assertion,声明用户的身份、认证情况和属性信息。
例如,断言可能表达:
- 用户的唯一标识。
- 用户认证发生的时间和方式。
- 用户邮箱、姓名或组织属性。
- 断言的有效时间范围。
- 断言的受众限制。
这些信息并不意味着 SP 可以不经校验直接信任。SP 必须按照 SAML 配置和协议要求验证响应与断言。
3. SAML 的安全校验
SAML 的安全实现比较复杂,常见的校验内容包括:
- 验证 XML 数字签名。
- 验证签名证书或可信密钥。
- 校验 Issuer。
- 校验 Audience,确认断言是发给当前 SP 的。
- 校验
NotBefore和NotOnOrAfter等有效时间条件。 - 校验
Destination、Recipient和适用的InResponseTo。 - 防止同一响应或断言被重复使用。
- 正确处理 XML 规范化与 XML Signature Wrapping 等攻击风险。
XML 签名验证不能只检查文档中“存在一个有效签名”。实现必须确保经过验证的签名确实覆盖了应用实际使用的 Assertion,并采用成熟的 SAML 库处理相关协议细节。
生产环境不建议自行编写 XML 签名解析和断言校验逻辑。
4. SAML 与 OIDC 的区别
| 对比项 | SAML | OIDC |
|---|---|---|
| 数据格式 | XML | 常见令牌为 JWT,协议数据也可能采用 JSON |
| 主要用途 | 企业身份联合、浏览器 SSO | Web、移动端、API 相关身份认证 |
| 典型客户端 | 企业 SaaS、传统企业应用 | 现代 Web 应用、移动应用、API 客户端 |
| 令牌或断言 | SAML Assertion | ID Token |
| 常见配置方式 | XML Metadata、证书、实体 ID | Discovery 文档、Issuer、Client ID、JWKS |
| 开发体验 | XML 与签名处理较复杂 | 更贴合现代 Web 与 API 技术栈 |
这并不意味着 SAML 已经过时。企业在选择身份认证方案时,往往需要优先考虑现有应用的兼容性、企业身份平台的支持能力和合规要求。
如果一个企业已有成熟的 SAML 身份体系,没有必要仅仅为了追求新技术就一次性替换所有系统。
六、CAS:经典的单点登录协议
CAS(Central Authentication Service)是一种经典的单点登录协议,最初在高校和企业内部系统中得到广泛应用。
在传统 CAS 架构中,CAS Server 负责统一认证,CAS Client 则集成在各个业务系统中。
例如,一个学校有教务系统、图书馆系统、选课系统和校园门户。用户只需要在统一认证中心登录一次,就可以访问支持 CAS 的多个系统。
1. CAS 的核心概念
CAS Server:统一认证服务器,负责登录、维护中央会话和签发服务票据。
CAS Client:业务系统中的 CAS 客户端,负责发起认证请求、校验票据并建立本地会话。
TGT(Ticket Granting Ticket):与用户在 CAS Server 上的登录会话相关联的票据。
ST(Service Ticket):面向某个具体业务服务签发的一次性票据,业务系统需要将其交给 CAS Server 校验。
不同 CAS 版本还可能涉及 Proxy Ticket 等其他票据类型。
2. CAS 登录流程
浏览器 业务系统 A CAS Server
| | |
|-- 访问系统 A ----->| |
|<-- 跳转 CAS -------| |
|------------------------------------------->|
|<-------------- 登录页面 -------------------|
|-------------- 提交凭证 ------------------->|
|<-------------- 登录成功 -------------------|
|<-------------- 返回 Service Ticket -------|
|-- 携带 Ticket 访问系统 A ----------------->|
| |-- 服务端校验 Ticket -->|
| |<-- 校验通过 ------------|
|<-- 建立系统 A 本地会话 --------------------|
用户登录 CAS Server 后,CAS Server 会建立中央登录会话。用户再访问另一个 CAS Client 时,浏览器通常会再次跳转到 CAS Server。
由于中央会话已经存在,CAS Server 可以不要求用户再次输入密码,而是为新的业务服务签发新的 Service Ticket。
业务系统必须通过规定的校验流程验证 ST,并检查票据是否与当前服务地址匹配。ST 通常具有短有效期且只能成功使用一次。
3. CAS 的特点
- 协议流程相对直观,适合传统 Web SSO。
- 通过中央认证服务器管理登录会话。
- 各业务系统可以建立自己的本地会话。
- 通常需要在业务系统中集成 CAS Client。
- 与现代移动端和 API 生态集成时,可能需要额外的协议适配。
CAS 和 SAML、OIDC 一样,都可以参与单点登录,但它们的协议消息、令牌模型和客户端集成方式不同。
CAS 不是 Session 或 JWT 的另一种叫法。CAS 是认证协议,而 Session 和 JWT 是实现登录状态管理或令牌传递时可能使用的机制。
七、SSO:单点登录的实现原理
SSO(Single Sign-On)即单点登录,是一种用户体验和身份管理能力:用户完成一次认证后,可以在一定条件下访问多个相互信任的业务系统,而不需要在每个系统中重新输入凭证。
SSO 不是单独的一种令牌格式,也不等同于某一个具体协议。
OIDC、SAML 和 CAS 都可以用于构建 SSO 系统。
1. 为什么单点登录可行?
假设一个企业有三个系统:
- OA 办公系统。
- CRM 客户管理系统。
- 财务管理系统。
每个系统各自维护用户密码,不仅增加了用户管理成本,也使离职员工账号回收和安全策略执行变得困难。
引入统一身份平台之后,架构可以变成:
统一身份平台
/ | \
/ | \
OA CRM 财务系统
用户访问 OA 时,OA 将用户引导至身份平台完成认证。
随后用户访问 CRM,如果 CRM 与同一个身份平台建立了可信关系,身份平台可能根据已有的中央会话直接完成认证,不要求用户重新输入密码。
但这里有一个容易忽略的细节:SSO 不意味着所有业务系统共享同一个 Session。
在常见的 OIDC 或 SAML 架构中:
- 身份平台维护自己的中央登录会话。
- 每个业务系统通常维护自己的本地会话。
- 身份平台负责向业务系统证明用户已完成认证。
- 业务系统根据认证结果建立自己的登录状态。
这种设计既能提供统一登录体验,也可以让业务系统独立管理本地会话和权限。
2. SSO 与统一身份管理
一个完整的企业身份平台,通常不只提供 SSO,还可能提供:
- 用户和组织管理。
- 多因素认证。
- 社交登录与外部身份源集成。
- OIDC、OAuth 2.0、SAML、CAS 等协议支持。
- 应用注册、回调地址和密钥管理。
- 角色与权限管理。
- 账号禁用、会话撤销和风险控制。
- 用户生命周期管理与目录同步。
对于开发团队来说,统一身份平台的价值不仅是让登录页面统一,更是将认证、身份生命周期管理和应用接入规范化。
八、LDAP:目录服务与身份数据
LDAP(Lightweight Directory Access Protocol)是一种访问目录服务的协议。
LDAP 本身并不是像 OIDC 那样的现代 Web 身份认证协议,但在企业身份体系中非常常见。
例如,企业可以在目录服务中维护:
- 用户账号。
- 用户姓名和邮箱。
- 部门与组织关系。
- 用户所属的组。
- 部分账号状态和目录属性。
业务系统或身份平台可以通过 LDAP 查询目录数据,也可能使用 LDAP Bind 等机制验证用户提供的凭证,具体取决于目录服务的配置和安全策略。
1. LDAP 与 OIDC、SAML 的区别
LDAP 主要解决的是如何访问目录数据,而 OIDC、SAML 主要解决的是如何在应用之间传递和验证身份认证结果。
例如,一个企业可以使用 Active Directory 作为员工目录,并部署一个支持 OIDC 的身份提供商。
整个流程可以是:
- 员工访问业务系统。
- 业务系统通过 OIDC 将用户引导到身份提供商。
- 身份提供商根据配置使用企业目录验证用户。
- 身份提供商签发 ID Token,并完成 OIDC 流程。
- 业务系统验证身份认证结果,建立本地会话。
这样,LDAP 和 OIDC 不是互相替代的关系,而是可以处于同一个身份体系的不同层次。
需要注意,LDAP Bind 并不天然等于完整的安全认证方案。实际部署仍需考虑 TLS、账号锁定、密码策略、目录访问权限和服务端安全配置。
九、MFA 与其他认证方式
除了认证协议之外,身份平台还需要决定用户通过什么方式证明自己的身份。
1. 常见认证因素
多因素认证(MFA)要求使用两个或更多不同类别的认证因素。
| 因素类别 | 示例 |
|---|---|
| 你知道的 | 密码、PIN |
| 你持有的 | 硬件安全密钥、认证器设备 |
| 你本身具备的 | 指纹、人脸等生物特征 |
密码加短信验证码在一些系统中也被视为多因素认证,但其安全性取决于短信通道和具体实现。短信可能受到 SIM 换卡、社工攻击和短信转发等风险影响。
如果业务安全要求较高,可以考虑支持 FIDO2/WebAuthn 等抗钓鱼认证机制。
2. 密码、短信与社交登录
这些方式解决的是用户如何完成身份验证,并不决定后续使用哪一种协议传递认证结果。
例如:
- 用户可以通过密码完成 OIDC 登录。
- 用户可以通过短信验证码完成 OIDC 登录。
- 用户可以通过企业目录账号完成 OIDC 登录。
- 用户可以通过外部社交身份提供商完成 OIDC 登录。
认证方式和认证协议属于不同层次。
十、常见协议如何选择?
在真实项目中,不应该仅仅因为某种协议比较新,就直接决定使用它。更合理的做法是根据应用类型、企业现有身份体系、客户端形态和安全要求进行选择。
| 场景 | 常见选择 | 原因 |
|---|---|---|
| 新建 Web 应用,需要第三方或企业账号登录 | OIDC | 身份认证语义明确,现代 Web 生态支持较好 |
| 前后端分离应用需要调用受保护 API | OAuth 2.0 + OIDC | 分别处理 API 授权和用户身份认证 |
| 企业已有大量支持 SAML 的 SaaS 系统 | SAML | 兼容现有企业身份联合体系 |
| 高校或传统内部系统已使用 CAS | CAS | 与现有系统兼容,迁移成本较低 |
| 企业需要集中管理员工账号和组织信息 | LDAP/目录服务 + 身份平台 | 目录数据可以为认证和身份管理提供基础 |
| 普通 Web 后台,所有业务都由同一个服务处理 | Cookie + Session | 实现简单,服务端会话管理直接 |
| 多个 API 服务需要验证访问令牌 | OAuth 2.0 + 合适的 Access Token 机制 | 可以根据受众、范围和服务边界进行授权 |
一个典型的现代企业身份架构可能是:
企业目录 / 外部身份源
|
v
统一身份平台
|
+---- OIDC ---- Web / 移动端应用
|
+---- SAML ---- 企业 SaaS
|
+---- OAuth 2.0 ---- API 授权
|
+---- LDAP ---- 目录查询或兼容系统
如果需要同时支持历史系统和现代应用,统一身份平台可以在不同边界上提供不同协议,而不必强迫所有业务系统使用同一种协议。
十一、后端开发中的关键安全实践
认证协议只是安全体系的一部分。真正容易出现问题的地方,往往在于令牌校验、会话管理、回调地址、权限判断和身份映射。
1. 严格校验令牌
对于 JWT 格式的 ID Token 或 Access Token,应根据具体令牌的用途和协议要求校验:
- 签名是否有效。
- 是否使用允许的签名算法。
iss是否是可信的签发者。aud是否包含当前客户端或资源服务器。exp、nbf等时间声明是否有效。- 对 OIDC ID Token,是否正确处理
nonce等认证请求关联参数。
不能因为一个令牌格式正确,或者能成功 Base64URL 解码,就信任其中的用户 ID。
不透明 Access Token 可能需要通过授权服务器提供的校验端点或其他服务端机制进行验证,而不是尝试将其当作 JWT 解析。
2. 保护授权回调
OAuth 2.0 和 OIDC 登录过程中,授权回调是非常重要的安全边界。
开发时应注意:
- 只允许预先注册且经过严格匹配的回调地址。
- 使用
state关联授权请求与响应。 - 对 OIDC 正确使用和验证
nonce。 - 对公开客户端使用 PKCE。
- 不要将授权码或令牌写入普通业务日志。
- 避免把回调参数泄露到第三方页面或分析系统。
- 正确处理授权失败、超时、重复回调和用户取消授权等情况。
不要通过简单的字符串包含判断来验证回调地址,例如允许任何包含 example.com 的 URL,这可能引入开放重定向等风险。
3. 防范 XSS 与 CSRF
如果浏览器通过 Cookie 维持登录状态,需要合理配置:
HttpOnly:降低 JavaScript 直接读取 Cookie 的风险。Secure:仅通过 HTTPS 发送 Cookie。SameSite:根据应用的跨站登录需求选择合适策略。
这些属性不能完全替代 CSRF 防护。对于依赖 Cookie 自动携带认证信息的状态变更请求,还应结合 CSRF Token、Origin/Referer 检查等措施设计防护。
如果将访问令牌保存在浏览器可被 JavaScript 读取的位置,XSS 攻击可能直接窃取令牌。因此,令牌存储方式应结合客户端架构和实际威胁模型选择。
对于服务端 Web 应用,一种常见方案是使用后端保存令牌、浏览器持有安全 Cookie 的模式,以减少浏览器直接接触令牌的机会。
4. 正确设计登出
登出不是简单地删除前端存储中的 Token。
一个完整的登出设计可能涉及:
- 清除业务系统的本地 Session。
- 按需要撤销 Refresh Token。
- 根据产品需求跳转到身份平台的登出端点。
- 清理或终止身份平台的中央会话。
- 根据系统架构处理其他业务系统的会话。
如果只退出某个业务系统,用户可能仍然保持着身份平台的中央登录状态。再次访问该系统时,身份平台可能会在无需重新输入密码的情况下完成认证。
这不一定是错误行为,而是需要提前定义清楚的产品语义。
5. 区分外部身份与内部用户
在接入 OIDC、SAML 或其他外部身份源时,不要直接假定外部用户的邮箱或用户名永远唯一且不会变化。
比较稳妥的做法是使用经过验证的外部身份标识建立映射。例如,对于 OIDC,通常可以使用 issuer + subject 作为外部身份关联的基础,再映射到系统内部的用户 ID。
外部身份
issuer + subject
|
v
身份映射关系
|
v
内部 user_id
|
v
角色与业务权限
需要注意,sub 的唯一性是在对应 Issuer 的上下文中理解的,不应脱离 Issuer 单独使用。
邮箱、手机号和用户名可以作为辅助属性,但不应未经验证就被当作可靠的跨系统身份主键。
此外,认证成功不代表拥有业务权限。外部身份平台证明用户是谁,业务系统仍需要独立判断用户可以访问哪些资源。
6. 做好密钥与令牌生命周期管理
生产环境还需要考虑:
- 使用 HTTPS 保护协议通信。
- 安全存储客户端密钥和签名私钥。
- 规划密钥轮换与旧密钥过渡期。
- 合理设置访问令牌和刷新令牌的有效期。
- 对敏感操作进行重新认证或强化认证。
- 记录必要的安全审计事件,但避免记录密码和完整令牌。
- 对账号禁用、权限变更和离职流程建立及时的生效机制。
如果使用 JWT 作为 Access Token,还需要认识到签名验证成功并不代表用户当前仍然处于启用状态。令牌撤销、权限变化和账号状态同步,需要结合系统的安全要求单独设计。
十二、总结
认证技术看起来有很多名词,但只要按照职责分层,整体关系就清晰得多。
- Cookie、Session:用于浏览器与服务端之间维持登录状态。
- JWT:一种令牌格式,可以承载声明并通过签名等机制保护完整性。
- OAuth 2.0:用于委托授权,让客户端在限定范围内访问受保护资源。
- OIDC:在 OAuth 2.0 之上增加标准化的用户身份认证能力。
- SAML:常见于企业级身份联合和传统 SaaS 单点登录。
- CAS:经典的中央认证与单点登录协议。
- SSO:一种跨应用共享认证体验的能力,可以通过多种协议实现。
- LDAP:用于访问目录服务,为企业账号、组织和身份属性管理提供基础。
- MFA:通过多个不同类别的认证因素增强身份验证安全性。
对于新建的 Web 和移动应用,如果需要统一身份认证和第三方登录,OIDC 通常是值得优先考虑的选择;如果需要对 API 进行授权,则应结合 OAuth 2.0 设计令牌和访问范围;如果要接入已有的企业系统,则应充分考虑 SAML、CAS 和 LDAP 等现有基础设施。
最终,选择认证技术的关键并不是追逐某一种协议,而是明确每一层需要解决的问题:如何验证身份、如何表达授权、如何维护会话、如何跨系统建立信任,以及如何在身份和权限发生变化时及时控制访问。
