SAML概念
前言
本文是 SAML(Security Assertion Markup Language)的概念篇笔记,目标是让对 SAML 完全不了解的读者一次性理解:
- SAML 是什么、解决什么问题
- 核心角色和概念
- 认证流程(SP-Initiated SSO 和 IdP-Initiated SSO)
- 请求报文(AuthnRequest)和响应报文(Response + Assertion)中各字段的详细含义
- 绑定方式、元数据、安全机制、单点登出等扩展知识
参考规范:
- 核心:SAML V2.0 Core
- 绑定:SAML V2.0 Bindings
- 元数据:SAML V2.0 Metadata
- 配置文件:SAML V2.0 Profiles
一、SAML 是什么
1.1 一句话定义
SAML(Security Assertion Markup Language,安全断言标记语言)是一种基于 XML 的开放标准,用于在身份提供方(IdP)和服务提供方(SP)之间交换身份认证与授权数据。
简单说:SAML 就是告诉你”这个用户是谁、他有什么权限、他什么时候登录的”的一套 XML 格式的协议。
1.2 诞生的背景
SAML 的历史最早可追溯到 2001 年(SAML 1.0),目前广泛使用的是 SAML 2.0(2005 年发布)。
在那个年代:
- 企业办公场景核心痛点:公司有几十个内部系统(OA、CRM、邮箱、HR 系统等),每个系统都有一套独立的账号密码,员工每天要在不同系统间反复登录。
- 当时的技术环境:XML 是数据交换的主流标准,JSON 尚未普及;Web Service / SOAP 盛行。
于是 SAML 应运而生,目的就是解决企业跨系统单点登录(SSO)问题——用户只需要在身份中心登录一次,所有接入系统都自动识别其身份。
1.3 核心价值
| 维度 | 没有 SAML | 有了 SAML |
|---|---|---|
| 登录方式 | 每个系统独立登录,N 套账号密码 | 一次登录,所有系统通用 |
| 身份管理 | 每个系统各自维护用户库 | 统一身份源(IdP)集中管理 |
| 安全管控 | 密码策略各自为政、无法统一审计 | 集中认证,可通过多因素认证(MFA)增强安全 |
| 新系统接入 | 新系统从头建用户体系 | 一键接入现有 IdP,零用户创建成本 |
1.4 SAML 解决的问题
SAML 主要解决三个问题:
- 认证(Authentication):证明”用户是谁”——即用户是否已成功登录。
- 属性传递(Attribute):传递用户的详细信息——如邮箱、部门、角色、工号等。
- 授权决策(Authorization Decision)(可选):告知 SP 用户是否有权限访问某资源。
对比 OIDC(OIDC 解决的是认证 + 用户信息获取),SAML 天然内置了认证和属性传递,且企业级能力更成熟。
1.5 SAML 与 OAuth2 / OIDC 的关系速览
| 维度 | SAML | OIDC | OAuth2 |
|---|---|---|---|
| 数据格式 | XML | JSON / JWT | JSON / 任意 |
| 主要用途 | 认证 + 属性传递 | 认证 | 授权 |
| 诞生时间 | 2005(SAML 2.0) | 2014 | 2012 |
| 传输方式 | HTTP-Redirect / POST / Artifact | HTTP + Bearer Token | HTTP + Bearer Token |
| 令牌 | SAML Assertion(XML) | ID Token(JWT) | Access Token |
| 移动端支持 | ❌ 差(依赖浏览器) | ✅ 好 | ✅ 好 |
| 适用场景 | 企业 SSO、B2B 联邦、政务金融 | 现代 Web/移动端 SSO、社交登录 | 开放平台 API 授权 |
详细对比可参考之前的文章:OAuth2、OIDC、SAML 协议对比
二、核心角色
SAML 定义了三个核心角色:
2.1 角色总览
1 | ┌──────────────────┐ ┌──────────────────┐ |
Principal(终端用户)
- 即用户本人,想要访问 SP 上的资源。
- 与 OIDC 中的 End-User / OAuth2 中的 Resource Owner 对应。
Service Provider(SP,服务提供方)
- 提供业务功能的应用或系统,如公司内部的 OA 系统、CRM、Jira、Confluence 等。
- SP 自己不存储用户密码,它信任 IdP 发来的身份声明。
- 对应 OIDC 中的 Relying Party(RP) / OAuth2 中的 Client。
Identity Provider(IdP,身份提供方)
- 统一管理用户身份的系统,存储用户名、密码、角色、权限等。
- 负责对用户做认证,并在认证成功后签发 SAML Assertion。
- 常见 IdP 产品:Active Directory Federation Services(AD FS)、Keycloak、Okta、OneLogin、Azure AD、PingFederate 等。
- 对应 OIDC 中的 OpenID Provider(OP) / OAuth2 中的 Authorization Server。
2.2 信任关系(Trust Relationship)
SP 和 IdP 之间需要提前建立信任,方式是通过交换元数据(Metadata):
1 | SP Metadata ──────► IdP (信任 SP 的签名) |
双向信任建立后:
- SP 信任 IdP 签发的 Assertion(因为 SP 持有 IdP 的公钥/证书,可以验签)。
- IdP 信任 SP 发起的 AuthnRequest(因为 IdP 持有 SP 的公钥,可以验签请求)。
这个信任建立是一次性配置——企业架构师完成一次元数据交换后,所有 SSO 流程的基础就搭建好了。
三、核心概念详解
在深入流程之前,需要先理解 SAML 的几个核心概念。这里采用”概念 + 类比”的方式帮助理解。
核心概念一览
本节共涉及 7 个核心概念,先给出总览表,方便有个整体印象后再逐一展开:
| 概念 | 一句话解释 | 类比 | 重要性 |
|---|---|---|---|
| Assertion(断言) | IdP 对用户身份作出的 XML 声明,SAML 的”令牌” | 🏢 公司开的”介绍信” | ⭐⭐⭐⭐⭐ 核心 |
| Protocol(协议) | 定义 SAML 请求-响应对的规范(如 SSO 用的认证请求协议) | 📮 信封上的格式规范 | ⭐⭐⭐⭐ 重要 |
| Binding(绑定) | 规定 SAML 消息如何通过 HTTP 传输(Redirect / POST / Artifact) | 🚚 信件的投递方式(平信/挂号/快递) | ⭐⭐⭐⭐ 重要 |
| Profile(配置文件) | 将角色、协议、绑定组合成完整的交互流程方案 | 🧾 完整的使用说明书 | ⭐⭐⭐ 有用 |
| NameID(用户标识) | SAML 中标识用户的唯一字符串,相当于用户的”ID 卡号” | 🆔 身份证号码 | ⭐⭐⭐⭐⭐ 核心 |
| EntityID(实体标识) | SP 或 IdP 的全局唯一标识符(类似 URL) | 🏪 店铺的注册编号 | ⭐⭐⭐ 有用 |
| 元数据(Metadata) | XML 文件,描述 SP/IdP 的端点、证书、能力等配置 | 📋 商家信息登记表(地址、公章等) | ⭐⭐⭐ 补充 |
其中 元数据(Metadata) 虽然同样重要,但由于内容较多且与请求/响应报文配合更紧密,将在第八节单独详述。
SAML 报文全貌(概念在 XML 中的位置)
在逐一展开前,先看一份完整的 SAML 报文结构关系图,清晰展示每个概念在 XML 报文中的具体位置和层级:
1 | ┌──────────────────────────────────────────────────────────────┐ |
下面逐一展开每个概念,每个概念都会给出其在 XML 报文中的完整结构:
3.1 Assertion(断言)
**Assertion 是 SAML 的”令牌”**,是 IdP 对用户身份所作出的声明——本质是一段 XML。
Assertion 在 XML 中的完整结构
1 | <saml:Assertion |
一个 Assertion 可以包含以下三类声明(至少包含一种):
① Authentication Statement(认证声明)
证明”用户在什么时间、通过什么方式完成了认证”。
1 | <saml:AuthnStatement AuthnInstant="2026-05-10T08:30:00Z" SessionIndex="abc123"> |
② Attribute Statement(属性声明)
传递用户的详细信息/属性。
1 | <saml:AttributeStatement> |
③ Authorization Decision Statement(授权决策声明)
告诉 SP 用户是否有权执行某个操作或访问某个资源(SAML 2.0 中较少使用,大多数场景靠 SP 自己鉴权)。
类比理解:可以把 SAML Assertion 想象成”一张盖了章的介绍信”。公司(IdP)为你出具介绍信,上面写着”此人身份已验证”(认证)、”他是工程部张三”(属性)、”他可以进入大楼三层”(授权)。你拿着这张信,大楼保安(SP)看到信上的公章(签名)是真的,就放你进去了。
3.2 Protocol(协议)
SAML 定义了一系列请求-响应对(Request-Response Pairs),称为 SAML Protocol。最常用的有:
| 协议 | 请求元素 | 响应元素 | 用途 |
|---|---|---|---|
| Authentication Request Protocol | <AuthnRequest> |
<Response> |
单点登录(SSO) ——这是最核心的协议 |
| Single Logout Protocol | <LogoutRequest> |
<LogoutResponse> |
单点登出(SLO) |
| Artifact Resolution Protocol | <ArtifactResolve> |
<ArtifactResponse> |
通过 Artifact 间接获取 Assertion |
| Name Identifier Management Protocol | <ManageNameIDRequest> |
<ManageNameIDResponse> |
管理用户标识映射 |
| Assertion ID Request Protocol | 无 Request | <AssertionIDResponse> |
通过 ID 直接查询 Assertion |
本文聚焦 Authentication Request Protocol(登录)和 Single Logout Protocol(登出)。
Protocol 在 XML 中的体现
Protocol 定义了 SAML 消息的最外层请求-响应包裹元素:
1 | <!-- 🔵 请求协议骨架:<samlp:AuthnRequest> --> |
命名空间约定:
samlp:=urn:oasis:names:tc:SAML:2.0:protocol— 协议层元素(请求/响应/状态码)saml:=urn:oasis:names:tc:SAML:2.0:assertion— 断言层元素(Assertion/Issuer/Subject/Conditions 等)
这是 SAML 2.0 的标准命名空间划分,所有实现都遵循此约定。
3.3 Binding(绑定方式)
Binding 规定了 SAML 协议消息(AuthnRequest、Response 等)应该如何通过 HTTP 传输。
常见的 Binding 有四种:
| Binding | 传输方式 | 请求方向 | 适用场景 |
|---|---|---|---|
| HTTP-Redirect | URL Query 参数(Base64 + 签名) | SP → IdP | SP 发起 AuthnRequest,轻量、适合 GET 重定向 |
| HTTP-POST | HTML 表单自动提交(POST Body) | IdP → SP 或 SP → IdP | 传输带签名的 Assertion(XML 可能很大),最常用 |
| HTTP-Artifact | 传一个短码(Artifact),SP 再后端直连取 Assertion | IdP → SP | 超长报文或有防中间人需求时 |
| SOAP | SOAP 协议(SOAP over HTTP) | SP ↔ IdP 服务端直连 | 单点登出、Artifact 解析等服务端场景 |
实际中最常见的组合是:
- SP → IdP:通过 HTTP-Redirect Binding 发送 AuthnRequest。
- IdP → SP:通过 HTTP-POST Binding 返回 SAML Response(含 Assertion)。
Binding 在 HTTP 层面的体现
HTTP-Redirect Binding(SP → IdP 传递 AuthnRequest):
1 | GET /sso/redirect? |
HTTP-POST Binding(IdP → SP 传递 Response):
1 | <!-- IdP 返回给浏览器的 HTML,页面加载后自动提交 --> |
HTTP-Artifact Binding(IdP → SP 先传短码,SP 再回连取 Assertion):
1 | # ① 浏览器收到的是短 Artifact 码 |
Binding 参数速查表:
| HTTP 参数 | 所属 Binding | 内容 | 处理方式 |
|---|---|---|---|
SAMLRequest |
HTTP-Redirect | AuthnRequest XML | Deflate 压缩 → Base64 → URL 编码 |
SAMLResponse |
HTTP-POST | Response XML | Base64 编码(不压缩) |
SAMLart |
HTTP-Artifact | Artifact 短码 | 固定长度字节码 |
RelayState |
Redirect / POST | 回跳 URL | 明文或 URL 编码 |
SigAlg + Signature |
HTTP-Redirect | 数字签名 | 仅 Redirect Binding 需在 URL 上签名 |
3.4 Profile(配置文件/流程模式)
Profile 定义了 SAML 在特定场景下的完整交互流程,它将角色、协议、绑定组合在一起形成可操作的方案。
SAML 2.0 定义了多种 Profile,最常见的是:
| Profile | 描述 |
|---|---|
| Web Browser SSO Profile | 最核心的配置——用浏览器做单点登录 |
| Single Logout Profile | 一次性登出所有已登录的应用 |
| Artifact Resolution Profile | 通过 Artifact 间接获取 Assertion |
| Name Identifier Mapping Profile | 跨域时的用户标识映射 |
| Assertion Query/Request Profile | SP 主动查询用户 Assertion |
日常开发中遇到的”SAML SSO“几乎都是指 Web Browser SSO Profile。
3.5 NameID(用户标识)
NameID 是 SAML 中标识用户的唯一字符串,相当于 OIDC 中的 sub(Subject)。
NameID 在 XML 中的完整结构
NameID 作为 <Subject> 的子元素出现,其完整上下文如下:
1 | <saml:Subject> |
NameID 元素各属性:
| 属性 | 含义 |
|---|---|
Format |
NameID 的格式类型(参见下方表格) |
NameQualifier |
限定该 NameID 的 IdP 的 EntityID(可选) |
SPNameQualifier |
限定该 NameID 有效的 SP 的 EntityID(可选) |
| 元素文本值 | 用户的唯一标识字符串 |
简洁用法(省去限定名):
1 <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">zhangsan@company.com</saml:NameID>
NameID 的常见格式(Format):
| Format URI | 示例值 | 说明 |
|---|---|---|
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified |
u12345 |
未指定格式,由 IdP 自定义 |
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress |
user@company.com |
邮箱格式 |
urn:oasis:names:tc:SAML:2.0:nameid-format:persistent |
abc-def-ghi-jkl |
持久的匿名标识(最推荐,企业场景默认) |
urn:oasis:names:tc:SAML:2.0:nameid-format:transient |
session-temp-xyz |
临时标识(每次登录不同,仅用于单次会话) |
urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName |
CN=张三,OU=Engineering,O=Company |
X.509 证书主题名 |
urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName |
COMPANY\zhangsan |
Windows 域用户 |
重要概念——NameID 持久性:
persistent:用户在 IdP 和 SP 之间的标识是固定的、持久的(不随会话改变)。即使清除了会话,下次登录同一个 SP,NameID 不变。这是推荐的企业场景选择。- **
transient**:每次 SSO 登录得到不同的 NameID,只对当前会话有意义,不需要跨会话保留。 - **
emailAddress**:直接用邮箱做标识,简单但存在用户更换邮箱后无法关联的风险。
3.6 EntityID(实体标识)
EntityID 是 SP 或 IdP 的全局唯一标识——通常是一个 URL(不一定要可访问,只是一个标识符)。
1 | SP 的 EntityID: https://myapp.company.com/saml/metadata |
EntityID 在 XML 中的体现
EntityID 在 SAML 报文中的多个位置出现,下面是其出现的所有位置:
1 | <!-- ① AuthnRequest 中的 Issuer:标识请求发起者(SP) --> |
关键校验规则:
- SP 在验签时,必须校验
Response/Issuer和Assertion/Issuer是否等于信任的 IdP 的 EntityID。 - SP 在校验
AudienceRestriction时,必须检查<Audience>中是否包含自己的 EntityID。 - AuthnRequest 中的
<Issuer>必须与 SP 元数据中配置的 EntityID 一致,IdP 据此查找 SP 的配置。
EntityID 在 SAML 中的作用相当于”身份证号”:
3.7 概念关系总结
1 | graph TB |
四、SAML SSO 认证流程
SAML 2.0 定义了两种典型的 SSO 流程:
4.1 SP-Initiated SSO(SP 发起登录)⭐ 最常用
场景:用户尝试访问一个 SP 应用(如 Jira),但尚未登录——SP 把用户踢到 IdP 登录,验证通过后跳转回来。
1 | sequenceDiagram |
流程详解(对应上述编号)
- 用户访问 SP:用户在浏览器中输入
https://jira.company.com。 - SP 检测未登录:SP 发现用户没有有效的本地会话,于是决定向 IdP 发起 SAML 认证请求。
- SP 重定向用户到 IdP:SP 构造一个
<AuthnRequest>XML,Base64 编码后通过 HTTP-Redirect Binding 让浏览器跳转到 IdP 的 SSO 端点。- 浏览器地址变成:
https://idp.company.com/SSO?SAMLRequest=xxxx&SigAlg=yyyy&Signature=zzzz
- 浏览器地址变成:
- 浏览器到达 IdP 登录页:IdP 解析 AuthnRequest,知道是哪个 SP 发起的请求,展示登录页面。
- 用户认证:用户输入账号密码(如果 IdP 已记住该用户的会话,此步可能跳过——这就是 SSO 的无缝体验)。
- IdP 生成响应:IdP 验证用户凭证后,生成包含
<Assertion>的<Response>,用 IdP 的私钥签名,然后通过 HTTP-POST Binding 返回给浏览器——一个包含 SAMLResponse 的自动提交 HTML 表单。 - 浏览器提交到 SP:浏览器自动将表单 POST 到 SP 的 ACS(Assertion Consumer Service)URL。
- SP 验签并建立会话:SP 接收到 SAML Response,使用 IdP 的公钥验证 XML 签名,校验 Assertion 中的各种条件(时间、Audience 等),确认无误后获取用户的 NameID 和属性,建立本地会话(如写入 Session Cookie)。
- 返回业务页面:用户已登录,SP 展示正常业务内容。
SSO 的精髓在第 5 步:如果用户已经在 IdP 有有效会话(如刚刚登录过其他系统),IdP 会自动签发 Assertion,用户无感知地完成所有 SP 的登录,这就是”一次登录,处处可用”。
4.2 IdP-Initiated SSO(IdP 发起登录)
场景:用户首先登录 IdP 的门户页面/应用列表页(如 Office 365 的 App Launcher),点击某个应用的图标,跳转到该应用(无需再登录)。
1 | sequenceDiagram |
IdP-Initiated 与 SP-Initiated 的区别:
| 对比维度 | SP-Initiated | IdP-Initiated |
|---|---|---|
| 谁先发起 | SP(用户点击了 SP 的链接) | IdP(用户从 IdP 门户点击应用图标) |
| 有 AuthnRequest 吗 | ✅ 有 | ❌ 没有(IdP 直接返回 Response) |
| 有无 RelayState | ✅ 有(跳回原页面) | ✅ 有(指定目标页面) |
| 安全性 | 更高(有请求-响应配对,IdP 可验证 request) | 较低(无请求关联,IdP 单方面发 Assertion) |
| 典型场景 | 用户直接访问业务系统被重定向 | 从公司门户/应用商店跳转到应用 |
4.3 RelayState 的作用
无论 SP-Initiated 还是 IdP-Initiated,流程中经常会出现一个叫 RelayState 的参数。
- SP-Initiated:RelayState 由 SP 在 AuthnRequest 中附带,告诉 IdP 认证成功后应该回到 SP 的哪个页面。例如用户访问
https://jira.company.com/projects/ABC被重定向,RelayState 就是https://jira.company.com/projects/ABC。 - IdP-Initiated:IdP 自己的门户点击某个应用时,RelayState 告诉 SP 需要跳转到应用的哪个具体页面。
1 | <!-- 通过 HTTP-POST 传递 RelayState --> |
提示:由于 RelayState 在请求/响应中明文传递,SP 应在校验 Assertion 后验证 RelayState 是否合法,防止被篡改劫持到恶意页面。
五、SAML 请求报文详解(AuthnRequest)
当 SP 需要让用户去 IdP 认证时,SP 构造一个 <AuthnRequest>。本节以 HTTP-Redirect Binding 为例,拆解其所有参数。
5.1 AuthnRequest 概览
1 | ┌─────────────────────────────────────────────────┐ |
5.2 完整示例(附带每行注释)
1 | <samlp:AuthnRequest |
5.3 AuthnRequest 属性详解
顶层属性
| 属性 | 必选? | 含义 | 说明 |
|---|---|---|---|
ID |
✅ 是 | 请求唯一标识 | UUID 格式(如 _a1b2c3d4-e5f6...)。IdP 可用于防重放日志跟踪。有些实现以”_“开头 |
Version |
✅ 是 | SAML 版本号 | 固定为 2.0 |
IssueInstant |
✅ 是 | 请求签发时间(UTC) | 格式为 YYYY-MM-DDTHH:MM:SSZ。IdP 用于判断请求是否在许可的时间窗口内 |
Destination |
✅ 是 | 目标 IdP SSO 端点 | IdP 应校验此 URL 是否与自己配置的 SSO 端点一致,防止请求被重定向到恶意地址 |
ProtocolBinding |
❌ 否 | 期望的响应 Binding | 告诉 IdP:你应该用哪种 Binding 返回 Response。可选值见下方 |
AssertionConsumerServiceURL |
❌ 否 | SP 的 ACS URL | IdP 将 Response POST 到这个地址。在 SP 元数据中提前配置过,此处可省略,但显式指定更明确 |
AssertionConsumerServiceIndex |
❌ 否 | ACS URL 索引 | 如果 SP 元数据中定义了多个 ACS URL,可以用索引值引用其中一个。与 AssertionConsumerServiceURL 互斥 |
AttributeConsumingServiceIndex |
❌ 否 | 属性消费服务的索引 | 引用 SP 元数据中定义的属性需求配置,指示 IdP 需要返回哪些用户属性 |
ForceAuthn |
❌ 否 | 是否强制重新认证 | false(默认):如果用户已有 IdP 会话,直接签发 Assertion。true:即使用户有会话,也必须重新输入密码(如财务系统敏感操作) |
IsPassive |
❌ 否 | 是否静默认证 | false(默认):正常显示登录页。true:IdP 不得与用户交互,如果无有效会话则返回错误(用于后台静默检查登录状态) |
ProviderName |
❌ 否 | 服务提供方名称 | 可读的 SP 名称,IdP 登录页可展示给用户看(如 “你正在登录 Jira”) |
ProtocolBinding 可选值
| URI | 含义 |
|---|---|
urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST |
IdP 通过 HTTP-POST 返回 Response(最常用) |
urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact |
IdP 通过 Artifact Binding 返回 Response |
urn:oasis:names:tc:SAML:2.0:bindings:PAOS |
用于 ECP Profile(扩展客户端代理) |
子元素详解
| 子元素 | 必选? | 说明 |
|---|---|---|
<Issuer> |
✅ 是 | SP 的 EntityID,IdP 据此查找该 SP 的元数据和配置 |
<Signature> |
❌ 否 | 数字签名。HTTP-Redirect Binding 一般使用 URL 参数签名(而非 XML 签名),HTTP-POST Binding 中可能包含 XML 签名 |
<Subject> |
❌ 否 | 指定某个特定用户。少用——一般 SSO 是让”当前操作的用户”去登录,而不是预先指定一个用户 |
<NameIDPolicy> |
❌ 否 | 指示 IdP 使用什么格式的 NameID(见上方 3.5 节的 NameID 格式表) |
<Conditions> |
❌ 否 | 请求有效期限制。通常只给一个很小的窗口(如 ±3 分钟),防止请求被截获后重放 |
<RequestedAuthnContext> |
❌ 否 | 要求 IdP 必须使用某种级别的认证方式——例如不能只用简单密码,必须使用 MFA |
<Scoping> |
❌ 否 | 用于 IdP 代理/链式认证场景,指定 IdP 可以向上游代理请求。企业内部场景很少用 |
<Extensions> |
❌ 否 | 扩展字段,用于厂商自定义参数。SAML 规范允许扩展,但不是标准 |
5.4 RequestedAuthnContext(认证强度要求)
AuthnContextClassRef 预定义的认证上下文 URI:
| URI | 含义 |
|---|---|
urn:oasis:names:tc:SAML:2.0:ac:classes:Password |
简单的密码认证 |
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport |
密码 + HTTPS 传输保护(最常见) |
urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract |
手机双因素 |
urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard |
智能卡认证 |
urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos |
Kerberos 认证 |
urn:oasis:names:tc:SAML:2.0:ac:classes:X509 |
证书认证 |
urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession |
基于之前的会话(用户已登录过) |
Comparison 属性的可选值:
| 值 | 含义 |
|---|---|
exact |
精确匹配——IdP 必须使用列表中的某个认证上下文 |
minimum |
最低要求——IdP 使用的认证上下文安全级别至少列表中的最低值 |
maximum |
最高限制——IdP 使用的认证上下文安全级别不超过列表中的最高值 |
better |
高于要求——IdP 使用的认证上下文安全级别应高于列表中所有值 |
5.5 通过 HTTP-Redirect Binding 传输 AuthnRequest
当使用 HTTP-Redirect Binding 时,AuthnRequest 以 URL 参数形式传递,而不是放入 URL Body。
URL 参数:
1 | https://idp.company.com/sso/redirect? |
| 参数 | 描述 |
|---|---|
SAMLRequest |
经过 deflate 压缩 + Base64 编码 的 AuthnRequest XML |
RelayState |
回跳地址,SP 认证成功后返回的页面 URL |
SigAlg |
签名算法 URI |
Signature |
SP 使用自己的私钥对 SAMLRequest+RelayState 的签名值,IdP 用 SP 公钥验签 |
注意:HTTP-Redirect 使用 URL 查询参数签名,而非 XML 数字签名。因为 URL 长度有限,SP 对 AuthnRequest 先进行 deflate 压缩再 Base64 编码。IdP 收到后先 Base64 解码再 inflate 解压还原 XML。
六、SAML 响应报文详解(Response + Assertion)
当 IdP 成功认证用户后,构造一个 <Response> 返回给 SP。这是 SAML 中最核心、最复杂的部分。
6.1 Response 概览
1 | ┌─────────────────────────────────────────────────┐ |
6.2 完整示例(附带每行注释)
1 |
|
6.3 Response 顶层属性详解
| 属性 | 必选? | 含义 | 说明 |
|---|---|---|---|
ID |
✅ 是 | 响应唯一标识 | UUID,SP 用于日志、去重 |
Version |
✅ 是 | SAML 版本 | 固定 2.0 |
IssueInstant |
✅ 是 | 响应签发时间(UTC) | SP 用于判断时效性 |
Destination |
✅ 是 | 目标 SP ACS URL | SP 必须校验此字段是否与自己的 ACS URL 一致,防止被发给错误的接收者 |
InResponseTo |
❌ 否 | 对应的 AuthnRequest ID | SP-Initiated 时有此字段,IdP-Initiated 时无此字段。SP 应校验此值是否与之前发送的请求 ID 匹配 |
6.4 状态码(Status)详解
<Status> 元素包含认证结果,由三部分组成:
StatusCode(状态码)
| 顶层状态码 | 含义 |
|---|---|
urn:oasis:names:tc:SAML:2.0:status:Success |
✅ 成功——认证通过,Assertion 有效 |
urn:oasis:names:tc:SAML:2.0:status:Requester |
❌ 请求方错误(SP 发起的请求有问题) |
urn:oasis:names:tc:SAML:2.0:status:Responder |
❌ 响应方错误(IdP 内部错误) |
urn:oasis:names:tc:SAML:2.0:status:VersionMismatch |
❌ 版本不匹配 |
子状态码(二级错误原因)
当顶层状态码为 Requester 或 Responder 时,子状态码提供更具体的原因:
| 子状态码 | 含义 |
|---|---|
AuthnFailed |
用户认证失败(密码错误等) |
InvalidAttrNameOrValue |
属性名称或值无效 |
InvalidNameIDPolicy |
NameIDPolicy 不满足 |
NoAuthnContext |
无法满足请求的认证上下文要求 |
NoAvailableIDP |
没有可用的 IdP(Scoping 代理场景) |
NoPassive |
IsPassive 请求无法满足(用户需要交互式登录) |
NoSupportedIDP |
没有支持的 IdP |
ProxyCountExceeded |
代理级数超限 |
RequestDenied |
请求被拒绝(一般是配置问题) |
RequestUnsupported |
不支持的请求类型 |
RequestVersionDeprecated |
请求版本已废弃 |
RequestVersionTooHigh |
请求版本过高 |
RequestVersionTooLow |
请求版本过低 |
ResourceNotRecognized |
资源无法识别 |
TooManyResponses |
响应过多 |
UnknownAttrProfile |
未知属性配置文件 |
UnknownPrincipal |
未知的 Principal |
UnsupportedBinding |
不支持的 Binding |
StatusMessage
可选的描述性文本,IdP 用于传入更多错误详情(如”User account is locked”)。
6.5 Assertion 核心字段详解
Issuer(断言签发者)
Assertion 中的 <Issuer> 必须是 IdP 的 EntityID。SP 应校验此值是否匹配信任的 IdP。
Subject(主体)
Subject 下的三大组件
① NameID
用户唯一标识。SP 通常用这个值作为用户在本地的唯一标识(如关联到数据库中的用户记录)。
② SubjectConfirmation(主体确认)
这是 SAML 重要的安全机制——防止 Assertion 被恶意 SP 或第三方劫持。它的工作方式如下:
Method 属性定义了确认方式:
| Method | 含义 |
|---|---|
...:cm:bearer |
Bearer(最常用):持有 Assertion 的 SP 即可使用——谁拿到 Assertion,谁就是被授权方。依赖 HTTP-POST 的接收者校验 |
...:cm:holder-of-key |
Holder-of-Key:SP 必须出示自己的私钥签名才能确认持有 Assertion(更严格,但实现复杂) |
...:cm:sender-vouches |
Sender-Vouches:由消息发送方担保,无需 SP 做额外操作,但需要信任传输信道 |
SubjectConfirmationData 中的属性:
| 属性 | 含义 | 说明 |
|---|---|---|
NotOnOrAfter |
✅ 确认数据过期时间 | SP 必须校验当前时间在此时间之前 |
NotBefore |
❌ 确认数据生效时间 | 在此之前 Assertion 无效 |
Recipient |
✅ 目标接收者的 URL | SP 必须校验此 URL 是否等于自己的 ACS URL——这是防止中间人劫持的核心手段 |
InResponseTo |
❌ 对应的请求 ID | SP 必须校验此值与之前发出的 AuthnRequest ID 一致——这是防重放的核心手段 |
Address |
❌ 发起请求的 IP 地址 | IdP 可以记录用户的来源 IP,SP 可做额外校验 |
⭐ 安全要点:SP 收到 Assertion 后,务必校验
Recipient、InResponseTo(如有)和NotOnOrAfter。很多 SAML 安全漏洞就是因为 SP 忽略了这些校验。
Conditions(条件)
| 属性 | 含义 |
|---|---|
NotBefore |
Assertion 生效时间(UTC) |
NotOnOrAfter |
Assertion 过期时间(UTC),通常比 AuthnInstant 晚 5~10 分钟(短有效期 = 更高的安全性) |
Condition 子元素
① AudienceRestriction(受众限制)⭐
1 | <saml:AudienceRestriction> |
- 作用:限定这个 Assertion 只能被指定的 SP 使用。
- SP 必须校验:检查
<Audience>中是否包含自己的 EntityID。如果不包含,必须拒绝该 Assertion。 - 这是防止”Assertion 注入”攻击的关键——防止 X SP 的 Assertion 被拿来登录 Y SP。
② OneTimeUse
标记该 Assertion 只能使用一次。SP 收到后应记录下来,再次收到相同 ID 的 Assertion 应拒绝。
③ ProxyRestriction
限制该 Assertion 还能被代理给多少个其他 SP(级联信任链场景)。Count 表示允许的最高级数。
AuthnStatement(认证声明)
| 属性 | 含义 |
|---|---|
AuthnInstant |
✅ 用户完成认证的时间(UTC)。重要!SP 可用此字段计算用户认证时长 |
SessionIndex |
❌ IdP 侧会话索引。在 SLO(单点登出)中非常关键——SP 和 IdP 通过此值关联同一次登录会话 |
SessionNotOnOrAfter |
❌ IdP 侧会话的过期时间 |
AuthnContext 中的认证强度 URI(与 AuthnRequest 中的一致,见 5.4 节)。
AttributeStatement(属性声明)
| 属性 | 含义 |
|---|---|
Name |
✅ 属性名——IdP 和 SP 之间约定好的字段名(如 email、department) |
NameFormat |
❌ 属性名的命名格式(见下方) |
FriendlyName |
❌ 可读属性名(对人友好,如 “Email”、”Department”) |
<AttributeValue> |
属性值,允许多个值(如多个角色) |
NameFormat 可选值:
| URI | 含义 |
|---|---|
urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified |
未指定,由 SP 和 IdP 自行约定 |
urn:oasis:names:tc:SAML:2.0:attrname-format:uri |
属性名是 URI 格式,如 urn:oid:0.9.2342.19200300.100.1.3(邮箱的 OID) |
urn:oasis:names:tc:SAML:2.0:attrname-format:basic |
最常用,属性名是简单字符串 |
常见标准属性(OID 映射):
| 常用属性 | OID | SAML 属性名 |
|---|---|---|
| 邮箱 | urn:oid:0.9.2342.19200300.100.1.3 |
mail 或 email |
| 姓名 | urn:oid:2.5.4.42 |
givenName |
| 姓氏 | urn:oid:2.5.4.4 |
sn |
| 工号 | urn:oid:2.16.840.1.113730.3.1.3 |
employeeID |
| 部门 | urn:oid:2.5.4.12 |
department |
| 电话 | urn:oid:2.5.4.20 |
telephoneNumber |
| 角色/组 | 自定义 | groups 或 memberOf |
实际企业场景中,SP 和 IdP 通过元数据(Metadata)协商确定 IdP 需要返回哪些属性,SP 需要消费哪些属性。
6.6 通过 HTTP-POST Binding 传输 Response
1 | <!-- IdP 返回给浏览器的 HTML 页面(自动提交表单) --> |
| POST Body 参数 | 描述 |
|---|---|
SAMLResponse |
完整的 SAML Response XML,经 Base64 编码 |
RelayState |
回跳地址。SP 应校验此值是否合法(防止开放重定向攻击) |
注意:HTTP-POST Binding 的 SA MLResponse 与 HTTP-Redirect Binding 的 SAMLRequest 不同——Response 不经过 deflate 压缩,只做 Base64 编码。因为 POST body 没有长度限制,不需要压缩。
6.7 SP 收到 Response 后的校验清单(必做)
SP 接收到 SAML Response 后,必须执行以下校验(任何一项失败,SP 必须拒绝该 Assertion):
1 | graph TD |
校验项详细说明
| 序号 | 校验项 | 为什么重要 |
|---|---|---|
| 1 | XML 签名验签 | 使用 IdP 公钥验证 XML 签名(Response 级别和/或 Assertion 级别),确保报文未被篡改 |
| 2 | Destination |
验证 Response 的 Destination 等于 SP 的 ACS URL,防止响应被非法重定向 |
| 3 | Status |
必须是 Success,否则 SSO 失败 |
| 4 | Conditions.NotBefore / NotOnOrAfter |
验证当前时间在 Assertion 有效期内(严格校验,时钟偏差可配置一个容忍窗口如 ±3 分钟) |
| 5 | AudienceRestriction |
验证 <Audience> 中包含 SP 的 EntityID,防止 Assertion 被其他 SP 使用 |
| 6 | SubjectConfirmationData.Recipient |
验证等于 SP 的 ACS URL,防止 Assertion 被截获后发给另一个 URL |
| 7 | SubjectConfirmationData.InResponseTo |
验证等于 SP 此前发出的 AuthnRequest ID(如果有),防重放 |
| 8 | SubjectConfirmationData.NotOnOrAfter |
验证确认数据未过期 |
| 9 | 重放检测 | 记录已使用的 Assertion ID,相同 ID 再次出现说明是重放攻击 |
| 10 | Issuer |
验证 Issuer 是受信任的 IdP 的 EntityID |
七、SAML 绑定方式详解
前面介绍了最常用的 HTTP-Redirect 和 HTTP-POST,这里再系统对比所有 Binding。
7.1 四种绑定对比
| Binding | 方向 | 传输方式 | 消息编码 | 优点 | 缺点 |
|---|---|---|---|---|---|
| HTTP-Redirect | SP → IdP | HTTP GET(302 重定向) | URL Query 参数,Deflate + Base64 | 轻量、适合浏览器重定向 | URL 长度受限(通常 8KB),只能单向 |
| HTTP-POST | 双向 | HTTP POST(HTML 表单) | POST Body,Base64 | 无长度限制,可传完整签名 XML | 涉及浏览器渲染 HTML 表单 |
| HTTP-Artifact | IdP → SP | 先传短 code,再服务端取 | Artifact 码 + SOAP 回连 | 适合超长报文,减少浏览器暴露 | 多一次服务端 HTTP 请求,延迟增加 |
| SOAP | SP ↔ IdP | 服务端直连 HTTP POST | SOAP Envelope | 服务端可靠通信 | 不支持浏览器参与 |
7.2 HTTP-Artifact Binding 详解
当 SAML Response 的 XML 过大(超过浏览器 URL/POST 限制)或者有更严格的安全要求时,可使用 Artifact Binding:
流程:
1 | 1. IdP 返回一个短的 Artifact 字符串(约 40 字符)给浏览器 |
Artifact 格式:
1 | Artifact = TypeCode (2字节) + EndpointIndex (2字节) + RemainingArtifact (20字节随机数) |
优缺点:
- ✅ 浏览器只传递短 code,敏感数据不经过浏览器(更安全)。
- ✅ 无长度限制。
- ❌ 需要 SP 能反向连接 IdP(防火墙可能限制出站)。
- ❌ 多一次 SOAP 调用,延迟增加。
八、元数据(Metadata)
SP 和 IdP 之间的信任关系通过交换元数据建立。元数据是一个 XML 文件,描述了实体的配置、端点、证书、能力等。
8.1 SP 元数据示例
1 |
|
8.2 IdP 元数据示例
1 |
|
8.3 元数据中的关键要素
| 字段 | 含义 |
|---|---|
entityID |
全局唯一的实体标识,是 SP/IdP 的”身份证号” |
KeyDescriptor[use=signing] |
签名用的证书(对方用于验签) |
KeyDescriptor[use=encryption] |
加密用的证书(对方用于加密,我方解密) |
AssertionConsumerService |
SP 接收 SAML Response 的端点(核心!SP 的”收件地址”) |
SingleSignOnService |
IdP 接收 AuthnRequest 的端点(核心!IdP 的”收件地址”) |
SingleLogoutService |
单点登出端点 |
8.4 元数据交换方式
| 方式 | 描述 |
|---|---|
| 手工导入 | 一方导出元数据 XML 文件,另一方导入到系统中。最常见的方式,适合 SP 和 IdP 数量都不多的情况 |
| URL 发布 | 通过 HTTP 发布元数据端点(如 https://idp.company.com/FederationMetadata/2007-06/FederationMetadata.xml),对方定时轮询获取 |
| 标准化注册 | 通过中间注册中心,如公司的元数据交换平台,自动分发 |
九、安全机制
SAML 的安全性保障主要来自以下几个方面。
9.1 XML 数字签名(XML Digital Signature / XMLDSig)
为什么需要签名?
浏览器传输 SAML 消息时(HTTP-Redirect 或 HTTP-POST),消息经过中间节点可能被篡改。签名可以保证:
- 完整性:报文未被篡改。
- 来源认证:报文确实来自合法的 IdP/SP。
签名结构概览:
1 | <Signature> |
关键点:
- XML 签名支持多种签名粒度:可以签整个 Response,也可以只签 Assertion,甚至可以只签特定元素。
- Canonicalization(规范化):XML 文档有多种等效写法(如标签换行、属性顺序等),规范化算法将其统一为标准形式,确保相同内容的 XML 产生相同的哈希值。
- 推荐使用 Exclusive XML Canonicalization(
xml-exc-c14n),避免命名空间声明干扰签名结果。
9.2 XML 加密(XML Encryption)
SAML 规范支持对 Assertion 中的敏感属性(如个人身份信息)进行加密,确保只有目标 SP 能解密。
- IdP 使用 SP 的公钥(来自 SP 元数据的
KeyDescriptor[use=encryption])对敏感 XML 元素加密。 - SP 使用自己的私钥解密。
9.3 短有效期
| 策略 | 典型值 | 说明 |
|---|---|---|
| AuthnRequest 有效期 | ±1~3 分钟 | 防止请求被截获重放 |
| Assertion 有效期 | 5~10 分钟 | 大多数 SSO 场景足够,短窗口降低重放风险 |
| Session 有效期 | 通常 8~12 小时 | 对应一个工作日,IdP 侧会话 |
9.4 Audience 限制与 Recipient 校验(双重防线)
- Audience 限制:Assertion 中的
<Audience>明确指定了哪个 SP 可以使用,其他 SP 收到后必须拒绝。 - Recipient 校验:
<SubjectConfirmationData>的Recipient指定了 ACS URL,即使 Assertion 落到其他系统手上,也会因为 URL 不匹配而校验失败。
9.5 签名算法的安全实践
| 算法 | 推荐? | 说明 |
|---|---|---|
| RSA-SHA256 | ✅ 推荐 | 当前主流标准 |
| RSA-SHA1 | ❌ 不推荐 | SHA-1 有碰撞风险 |
| DSA-SHA1 | ❌ 不推荐 | 已逐步废弃 |
| RSA-SHA512 | ✅ 可以用 | 更安全但部分旧 IdP 可能不支持 |
十、单点登出(SLO - Single Logout)
SAML 支持”一次登出,全部登出”的 SLO 流程。
10.1 SLO 基本流程
1 | sequenceDiagram |
10.2 LogoutRequest 示例
1 | <samlp:LogoutRequest |
10.3 SLO 的两种传输方式
| 方式 | 描述 | 优缺点 |
|---|---|---|
| Front-Channel(前端通道) | 通过浏览器重定向或 POST 传递(用户可见) | 依赖浏览器,用户体验受影响 |
| Back-Channel(后端通道) | SP 和 IdP 之间通过 SOAP 服务端直连 | 更快更可靠,用户无感知。但要求 SP 服务端能连接 IdP |
实际上,SLO 的实现比 SSO 复杂得多,很多 IdP 和 SP 的 SLO 实现并不完美,可能出现”部分登出”(部分 SP 成功、部分失败)的情况。企业场景中,更常见的策略是让 IdP 侧会话过期自动登出所有 SP。
十一、SAML 的优缺点总结
11.1 优点
| 优点 | 说明 |
|---|---|
| 成熟稳定 | SAML 2.0 自 2005 年发布,已在全球大量企业部署,经得起考验 |
| 企业生态丰富 | 几乎所有企业级 Saas(Salesforce、Workday、Office 365、Jira、Confluence)都支持 SAML SSO |
| 单点登录体验好 | 一次登录,多系统可用(在 Web 场景下体验非常流畅) |
| 安全机制完善 | XML 签名、加密、短有效期、Audience 限制、Recipient 校验等多层防护 |
| 属性传递标准 | 可附带丰富的用户属性(角色、部门、组等),SP 无需额外 API 调用 |
| 联邦场景成熟 | 支持跨组织的身份联盟(B2B),不同公司的 IdP 可互相操作 |
11.2 缺点
| 缺点 | 说明 |
|---|---|
| XML 冗长复杂 | SAML 报文比 JWT 大得多,解析和签名验证的开销也更大 |
| 移动端支持差 | 依赖浏览器和 HTTP-POST/Redirect 绑定,原生 App 中实现 SAML 困难(需要用 WebView,体验不佳) |
| 实现难度高 | 完整实现 SAML 规范(处理各种签名、绑定、编码方式)远比 OIDC 复杂 |
| 灵活性不足 | SAML 主要围绕”浏览器-重定向-Backend”模型,对 SPA、前后端分离场景支持差 |
| 无 API 授权能力 | SAML 专注于认证,不做 API 授权(不像 OAuth2/Access Token 那样可用于鉴权微服务调用) |
11.3 何时选 SAML,何时选 OIDC?
| 场景 | 推荐 |
|---|---|
| 公司接入 Salesforce、Jira、Slack 等企业 SaaS | SAML(这些系统通常只支持 SAML) |
| 新建设的企业内部 SSO 系统 | OIDC(现代、轻量、生态好) |
| 移动端 App 登录 | OIDC(不要用 SAML) |
| 政府/金融/合规严格的场景 | SAML(成熟、审计链路清晰) |
| 零信任架构、微服务鉴权 | OIDC / OAuth2 |
| B2B 跨公司联邦登录 | SAML(生态成熟)或 OIDC(新建可选) |
十二、小结
- SAML(Security Assertion Markup Language)是一种基于 XML 的企业级认证+属性传递协议,主要解决”一次登录,多处访问”的 SSO 问题。
- 三大核心角色:用户(Principal)、服务提供方(SP,如业务系统)、身份提供方(IdP,如统一认证中心)。
- 核心流程为 SP-Initiated SSO(SP 发起,最常用)和 IdP-Initiated SSO(IdP 门户发起)。
- AuthnRequest 是 SP 发给 IdP 的认证请求,包含 SP 身份、期望的 NameID 格式、认证强度要求等。
- Response + Assertion 是 IdP 返回给 SP 的”身份令牌”,包含用户唯一标识(NameID)、认证时间、认证方式、用户属性,以及 Audience 限制、Recipient、签名等安全机制。
- SP 收到 Response 后必须执行严格的校验(签名、时间、Audience、Recipient、重放检测等),任何一项校验失败都应拒绝。
- 元数据(Metadata)是 SP 和 IdP 之间建立信任的基石,通过交换 XML 元数据来同步端点、证书和配置。
- 安全机制主要依赖 XML 数字签名、短有效期、Audience 限制和 Recipient 校验。
- SLO(单点登出)实现比 SSO 复杂,实际企业场景需仔细测试。
- 新系统优先选择 OIDC,但接入企业现有 SaaS / 合规要求高的场景仍大量使用 SAML。
SAML 虽然诞生已有二十年,XML 也很”重”,但在企业级 SSO 和 B2B 联邦领域,它依然是不可替代的事实标准。理解 SAML 的核心概念和报文细节,对于企业架构师、SaaS 集成开发和身份认证领域的工程师来说,仍然是一项重要的技能。
