目录

  1. SAML 是什么,解决什么问题
  2. SAML 认证流程
  3. 信任建立:元数据与关键字段
  4. 元数据交换方式
  5. 安全机制:签名、证书与校验
  6. 安全最佳实践

1. SAML 是什么,解决什么问题

SAML(Security Assertion Markup Language) 是一种基于 XML 的身份联合协议,主要用于在身份提供方(IdP)和服务提供方(SP)之间交换身份信息。

它解决的核心问题是跨系统单点登录(SSO):用户只需在 IdP 完成一次认证,即可访问多个 SP,无需重复登录。典型场景包括企业内部多系统统一登录、跨组织身份联合,以及传统 Web 应用接入集中式身份入口。

SAML、OAuth 2.0、OIDC 三者经常一起被提到,但各自解决的问题不同。SAML 解决跨域身份认证,回答”这个用户是谁”;OAuth 2.0 解决授权委托,回答”这个应用能代表用户访问哪些资源”;OIDC 构建在 OAuth 2.0 之上,为身份认证和用户身份声明提供了标准化层。三者的关系可以概括为:SAML 主要用于身份认证,OAuth 2.0 主要用于授权,OIDC 则是在 OAuth 2.0 之上补充标准化认证能力。


2. SAML 认证流程

SAML 认证主要涉及三个参与方:用户浏览器、身份提供方(IdP)和服务提供方(SP)。认证过程中传递的核心消息对象包括:AuthnRequest(认证请求)、Response(协议响应)、Assertion(身份声明)和 RelayState(登录前上下文)。其中 Assertion 是核心——SP 是否接受登录结果,取决于它是否通过了对 Assertion 的校验。

2.1 SP 发起登录(SP-Initiated SSO)

这是最常见的对接方式:用户先访问业务系统,系统发现用户尚未登录,再跳转到 IdP 完成认证。

SAML SP 发起登录流程图

对应的处理步骤是:

  1. 用户访问 SP 的受保护资源。
  2. SP 发现没有本地会话,生成 AuthnRequest,并通过浏览器重定向到 IdP 的 SSO 端点。
  3. 浏览器访问 IdP 的 SSO 端点,发起认证流程。
  4. IdP 完成用户认证,并生成带签名的 SAML Response,或其中包含已签名的 Assertion,具体取决于对接配置。
  5. IdP 通过浏览器返回自动提交表单,其中包含 SAML Response
  6. 浏览器将该响应提交到 SP 的 ACS URL。
  7. SP 验签并校验断言条件,通过后创建本地会话。

SAML 的消息往返通常借助浏览器完成,真正建立信任的是 IdP 与 SP 之间的证书和配置。

2.2 SAML Response 的核心结构

Response 是协议层容器,真正承载身份信息的是 Assertion

<samlp:Response>
    <saml:Issuer>https://idp.example.com/SAML2</saml:Issuer>

    <ds:Signature>
        <ds:SignatureValue>...</ds:SignatureValue>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509Certificate>MIICxDCCA...</ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>

    <saml:Assertion>
        <saml:Issuer>https://idp.example.com/SAML2</saml:Issuer>
        <saml:Subject>
            <saml:NameID>user@example.com</saml:NameID>
        </saml:Subject>
        <saml:Conditions
            NotBefore="2026-03-31T09:00:00Z"
            NotOnOrAfter="2026-03-31T09:05:00Z">
            <saml:AudienceRestriction>
                <saml:Audience>https://sp.example.com/SAML2</saml:Audience>
            </saml:AudienceRestriction>
        </saml:Conditions>
        <saml:AuthnStatement AuthnInstant="2026-03-31T08:59:30Z"/>
        <saml:AttributeStatement>
            <saml:Attribute Name="email">
                <saml:AttributeValue>user@example.com</saml:AttributeValue>
            </saml:Attribute>
        </saml:AttributeStatement>
    </saml:Assertion>
</samlp:Response>

这个结构里,SP 至少需要关注四类信息:

  • 签发方是谁Issuer 是否与受信任的 IdP 一致。
  • 用户是谁NameID 或属性映射是否满足本地用户识别规则。
  • 断言是否仍然有效NotBeforeNotOnOrAfter 是否通过校验。
  • 断言是否发给当前 SPAudience 是否匹配当前 SP 的 Entity ID

2.3 IdP 发起登录(IdP-Initiated SSO)

有些场景下,用户先进入统一门户(如企业内部导航页),再从门户点击进入某个业务系统。这类流程由 IdP 直接发起,SP 不会预先生成 AuthnRequest

SAML IdP 发起登录流程图

对应的处理步骤是:

  1. 用户已在 IdP 完成认证,点击门户中的业务系统入口。
  2. IdP 直接生成带签名的 SAML Response,或其中包含已签名的 Assertion,无需等待 SP 的请求。
  3. IdP 通过浏览器返回自动提交表单,其中包含 SAML Response
  4. 浏览器将该响应以 HTTP POST 的方式提交到 SP 的 ACS URL,通常还会附带 RelayState 指示目标页面。
  5. SP 收到响应后,执行与 SP 发起流程相同的校验:验签、校验 Issuer/Audience/时间窗口等。
  6. 校验通过后建立本地会话,将用户跳转到 RelayState 指定的目标页面。

与 SP 发起方式相比,IdP 发起流程省去了 AuthnRequest 的生成和重定向环节,SP 的处理逻辑更简单,但工程上有几点需要额外关注:

  • 通常缺少可用于请求绑定的 InResponseTo:SP 发起方式中,响应里通常会带有可用于关联本次 AuthnRequestInResponseTo 信息,SP 可以据此验证响应是否对应当前请求。IdP 发起时通常不存在这类请求绑定信息,SP 无法做同样的请求关联校验,防重放需要依赖其他手段(如断言 ID 去重)。
  • RelayState 需要完整性保护:IdP 发起场景下,RelayState 通常承载了目标 URL,如果未做保护,攻击者可以替换它实现跳转篡改。
  • 深链接支持有限:用户直接访问业务系统某个具体 URL 时,IdP 发起方式无法自然地保留该上下文,需要额外设计目标地址的传递机制。

3. 信任建立:元数据与关键字段

流程能够运行,并不等于 SP 会接受这次登录结果。SP 信任 IdP 发来的响应,前提是双方已经交换元数据,并基于证书建立了信任关系。

3.1 元数据是什么

元数据可以看作 SAML 对接双方的机器可读配置说明书。它描述了一个实体是谁、暴露哪些端点、使用什么证书、支持哪些绑定方式。

对接时,双方通常先交换元数据,再根据元数据完成信任配置:

3.2 IdP 元数据通常包含什么

对 SP 来说,IdP 元数据定义了受信任实体、登录端点和验签证书。

<EntityDescriptor entityID="https://idp.example.com/SAML2">
    <IDPSSODescriptor>
        <KeyDescriptor use="signing">
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>MIICxDCCA...</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </KeyDescriptor>
        <SingleSignOnService
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
            Location="https://idp.example.com/SAML2/SSO/Redirect"/>
        <NameIDFormat>
            urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
        </NameIDFormat>
    </IDPSSODescriptor>
</EntityDescriptor>

IdP 元数据中的关键内容有:

  • 实体标识:用于确认这是哪个 IdP。
  • 端点地址:SP 知道该把认证请求发到哪里。
  • 签名证书:SP 用它来验证 IdP 发来的响应或断言是否可信。

3.3 SP 元数据通常包含什么

对 IdP 来说,SP 元数据定义了响应投递地址、断言目标和请求签名或断言加密等能力。

<EntityDescriptor entityID="https://sp.example.com/SAML2">
    <SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true">
        <AssertionConsumerService
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
            Location="https://sp.example.com/SAML2/ACS"
            index="0"
            isDefault="true"/>
        <KeyDescriptor use="signing">
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>MIICxDCCA...</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </KeyDescriptor>
        <KeyDescriptor use="encryption">
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>MIICxDCCA...</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </KeyDescriptor>
    </SPSSODescriptor>
</EntityDescriptor>

工程上需要关注以下配置约束:

  • 签名证书加密证书可以相同,也可以分开配置。
  • AuthnRequestsSigned="true" 表示 SP 发出的认证请求需要签名,IdP 也应具备验签配置。
  • WantAssertionsSigned="true" 表示 SP 要求断言必须签名,但前提是 SP 的校验逻辑确实按这个要求执行。

4. 元数据交换方式

前面提到的 Entity IDSSO URLACS URL、签名证书,以及 AuthnRequestResponse 依赖的绑定方式,最终都要通过某种方式在 IdP 和 SP 之间交换并保持一致。手工填写参数也能完成对接,但生产环境更适合让这些信息以元数据的形式成组维护,从而减少人工拼装配置带来的错配风险。不同阶段的系统适合不同的交换策略,选择标准通常是风险控制和运维能力。

阶段 交换方式 核心特点 适用判断
POC / 测试 手动配置参数 先跑通链路,配置直接 适合短周期验证和联调,不适合长期维护
生产首次对接 线下交换元数据文件 便于审核,边界清晰 适合首次建立信任或安全要求较高的场景
生产稳定运行 元数据 URL 自动获取 便于同步,利于轮换 适合运维流程成熟、需要持续更新的环境

4.1 手动配置参数

在联调初期,如果目标是先把登录链路跑通,手动配置参数通常是最快的方式。它本质上是把元数据中的关键项拆开后逐项填写,优点是快,缺点是容易漏项和错配。

通常需要手工确认的参数包括:

  • Entity ID
  • SSO URL / ACS URL
  • 签名证书
  • NameID 格式
  • 用户属性映射规则

适用于功能验证、联调初期和临时测试环境。

这类配置不适合长期用于生产环境,主要问题是:

  • 证书更新容易遗漏。
  • 端点变更缺少统一同步机制。
  • 参数分散在多个配置项中,排查成本高。

4.2 线下交换元数据文件

当系统已经不只是验证“能否登录成功”,而是要正式建立可审核的信任关系时,线下交换元数据文件是生产首次对接中最常见、也最稳妥的方式。双方导出元数据,通过可信渠道交换并人工审核。

IdP 导出元数据  ──► 安全传输 ──► SP 导入并审核
SP 导出元数据   ──► 安全传输 ──► IdP 导入并审核
                      ↓
                 属性映射确认
                      ↓
                   联调测试
                      ↓
                    上线

审核重点包括:

  • 证书用途和有效期是否正确。
  • Entity ID 是否唯一且与预期一致。
  • ACS URLSSO URL 是否与实际环境匹配。
  • 支持的绑定方式是否与双方实现一致。

4.3 元数据 URL 自动获取

系统进入稳定运行阶段后,关注点通常会从“首次对接是否成功”转向“证书轮换和端点变更能否平滑处理”,这时元数据 URL 能显著降低维护成本。

这种方式依赖以下前提:

  • 拉取链路本身可信,例如使用受信任的 HTTPS。
  • 对元数据内容有校验和审核策略,避免盲目信任远端更新。
  • 证书轮换过程支持新旧证书并存的过渡期。

证书轮换通常按以下顺序进行:

  1. IdP 生成新证书。
  2. 元数据中同时发布新旧证书。
  3. SP 拉取更新后,开始同时信任两张证书。
  4. IdP 切换为新证书签名。
  5. 观察一段稳定期后,再从元数据中移除旧证书。

这种“重叠信任窗口”是生产环境平滑切换的关键。如果新证书发布后立即替换旧证书,而 SP 尚未完成同步,验签失败会直接影响登录。


5. 安全机制:签名、证书与校验

无论是前面提到的元数据交换、证书轮换,还是 SP 对 ResponseAssertion 的消费,本质上都建立在同一套底层安全机制之上:数字签名提供来源可信和防篡改保证,证书承载公钥并建立信任锚点,校验逻辑确保接收方只接受合法的消息和断言。

5.1 数字签名的作用与原理

签名在 SAML 中解决两个问题:

  • 来源可信:这份消息确实由持有对应私钥的一方签发。
  • 内容未被篡改:消息在传输和处理中没有被修改。

数字签名不应简化为“用私钥加密整段数据”。实际过程包括:

  1. 对待签名内容做规范化处理。
  2. 计算摘要。
  3. 使用私钥对摘要相关信息生成签名值。
  4. 接收方使用公钥验证签名,并对原始内容重新计算摘要比对。
签名方                                 验证方
 │                                      │
 │ 1. 对待签名内容计算摘要               │
 │ 2. 使用私钥生成签名值                 │
 │──────── 消息 + 签名 ───────────────►│
 │                                      │
 │                          3. 用公钥验证签名
 │                          4. 重新计算摘要
 │                          5. 比对摘要是否一致
 │                          6. 一致则验签通过

这一机制同时适用于 SAML 消息签名(如签名落在 ResponseAssertion,或两者同时签名)和元数据签名(如 IdP/SP 对自身元数据文档签名)。

5.2 证书在 SAML 各环节中的使用

场景 证书来源 作用
IdP 对响应或断言签名 IdP 元数据 SP 用于验签,确认响应可信
SP 对认证请求签名 SP 元数据 IdP 用于验签,确认请求来自受信任的 SP
IdP 加密断言 SP 元数据中的加密证书 只有持有对应私钥的 SP 能解密断言
元数据自身签名 签发方的签名证书 接收方验证元数据在传输中未被篡改

5.3 元数据交换的安全保障

元数据承载了证书和端点等关键信任配置,如果元数据本身在交换过程中被篡改,后续所有 SAML 消息的安全性都会失效。

保障元数据可信的常见手段包括:

  • HTTPS 传输:通过受信任的 TLS 链路拉取元数据 URL,防止中间人替换内容。
  • 元数据签名:SAML 元数据支持 XML 签名(<ds:Signature>),接收方在导入前验证签名,确认元数据来自预期实体且未被修改。
  • 人工审核:线下交换场景中,接收方应对元数据中的证书指纹、端点地址和 Entity ID 逐项核对,确认与预期一致后再导入。
  • 变更监控与审计:元数据 URL 自动获取场景下,应记录每次拉取到的元数据变更(如证书更新、端点变化),并在关键变更时触发告警或人工确认。

5.4 SAML 断言的完整校验

工程上常见的问题是:系统只验证签名是否正确,没有把断言约束一起检查完。可靠的校验通常至少包括:

  • 验证签名是否来自受信任证书。
  • 确认被签名的对象就是当前要消费的 ResponseAssertion
  • 验证 IssuerAudienceDestinationRecipient 等约束。
  • 验证时间窗口,包括 NotBeforeNotOnOrAfter,并处理合理的时钟偏差。
  • 如有 InResponseTo,确认它能对应到本次请求。

签名验证解决可信性,断言条件校验解决有效性与适用范围。 两者缺一不可。


6. 安全最佳实践

SAML 对接能否长期稳定运行,取决于边界条件是否处理完整,不能只看“是否能登录成功”。

6.1 证书与密钥管理

实践 原因
定期轮换证书 降低长期暴露风险,并为到期前切换预留窗口
私钥受控存储 私钥一旦泄露,攻击者可伪造受信任消息
区分签名与加密用途 有助于职责分离和后续轮换管理
建立过期告警 避免因证书过期导致大面积登录故障

6.2 协议与实现安全

实践 原因
强制使用 HTTPS 防止中间人攻击和明文泄露
要求并验证签名 既要配置要求,也要在代码或网关中真正校验
校验断言时间窗口 防止过期断言或超前断言被错误接受
校验 AudienceDestination 防止断言被错误投递或跨系统滥用
处理时钟偏差 分布式环境中应允许合理的 clock skew
RelayState 做完整性保护 防止被利用进行跳转篡改或状态混淆

6.3 常见问题排查

问题 常见原因 排查方向
验签失败 证书不匹配、旧证书未下线、签名对象不一致 检查元数据、证书用途、签名位置和证书轮换状态
Issuer 不匹配 Entity ID 配置不一致 比对元数据中的 entityID 与消息中的 Issuer
Audience 校验失败 SP 标识配置错误 核对 SP 的 Entity ID 与断言中的 Audience
ACS URL / Destination 错误 环境地址变更未同步 检查网关、反向代理和元数据中的端点配置
时间校验失败 服务器时钟不同步 检查 NTP、时区配置和允许的时钟偏差