Midnight's blog

返回

第一次接触企业身份系统时,最容易混淆的不是协议细节,而是把系统、登录、授权和账号同步当成一件事。

IAM 管的是身份的整个生命周期,包括账号从哪里来、如何登录、能访问什么、调岗时怎样改权限、离职后怎样停用,以及事后如何追查。SAML、OIDC 和 CAS 主要解决登录与单点登录。OAuth 2.0 解决应用访问 API 时的授权问题。SCIM 负责在系统之间创建、更新和停用账号。

Keycloak、Microsoft Entra ID、Okta 和 Apereo CAS 是产品或软件。SAML、OIDC、OAuth 2.0、CAS 和 SCIM 是协议。配置页面里的 ACS、Authorization Endpoint、Token Endpoint 和 SCIM Base URL,才是这些协议在系统中的具体接口。

一、IAM 系统#

没有统一管理时,OA、邮箱、代码仓库和 VPN 各自保存一份用户信息,也各有一套登录页面。新员工入职后,HR 发邮件通知多个管理员开账号,有的当天完成,有的拖到下周。离职同样靠人工通知。只要漏掉一个系统,旧账号就可能继续使用。

IAM 把这些分散的动作串起来。HR 录入工号、部门和邮箱后,系统按照岗位自动创建账号并分配权限。员工登录一次,就能进入已经接入单点登录的应用。调岗时撤掉旧权限,补上新权限。离职时停用登录账号,同时通知下游应用关闭账号。已经签发的会话和令牌是否立即失效,还要看 IdP 与各应用是否支持相应的注销或撤销机制。

人员信息通常保存在目录服务中,常见选择包括 Active Directory、LDAP 和云目录。很多公司以 HR 系统中的在职记录为准,再把必要字段同步到目录。目录保存工号、邮箱、部门和用户组,但它本身不等于登录协议,也不一定直接向业务系统发送 SAML Assertion 或 OIDC Token。

真正处理登录的是 IdP(Identity Provider,身份提供方)。IdP 校验密码、短信验证码或硬件密钥,必要时要求 MFA(多因素认证),然后向业务系统返回登录结果。用户点击“使用公司账号登录”后看到的页面,通常就是 IdP 的登录页面。业务系统不再保存或校验企业密码,只验证 IdP 提供的登录证明。这种一次登录后访问多个系统的体验,就是 SSO(Single Sign-On,单点登录)。

登录成功不代表拥有所有权限。认证回答“你是谁”,授权回答“你能做什么”。有的系统使用用户组和角色控制权限,有的把授权规则交给独立的策略服务。无论采用哪种方式,都不能只验证用户已经登录,还要判断他是否可以进入当前应用,以及是否可以执行当前操作。

账号创建、资料更新、权限变更和停用,也应当同步到下游系统。只接入单点登录,却没有处理离职账号和已有会话,解决的只是登录入口统一,不是完整的身份管理。IAM 还需要保留审计记录,例如谁在什么时间获得了哪项权限、由谁批准,以及这项权限是否仍有保留的必要。

这些能力可以集中在一个产品中,也可以由多个系统配合完成。Microsoft Entra ID 和 Okta 常用于企业身份管理与云应用单点登录。Keycloak 适合自行部署。Apereo CAS 最早广泛用于高校,如今也能支持 SAML、OIDC 和 OAuth。选择产品时,应先确认人员信息从哪里进入目录、登录使用什么协议、权限保存在哪里,以及离职状态能否及时传递给所有相关系统。

sequenceDiagram participant HR as HR 系统 participant IAM as IAM participant IdP as IdP participant App as 业务系统 actor User as 用户 HR->>IAM: 入职,写入人员信息 IAM->>IdP: 创建登录账号 IAM->>App: 创建账号并分配权限 User->>App: 打开系统 App->>User: 未登录,跳转到 IdP User->>IdP: 登录,必要时完成 MFA IdP->>App: 返回登录结果 App-->>User: 建立本地会话并进入系统 HR->>IAM: 调岗 IAM->>IdP: 更新用户组 IAM->>App: 更新角色和权限 HR->>IAM: 离职 IAM->>IdP: 停用登录账号 IAM->>App: 停用账号

IdP 与浏览器、业务系统之间,通常使用 SAML、OIDC 或 CAS 传递认证结果。应用需要访问 API 时使用 OAuth 2.0。IAM 或 IdP 要把账号同步到下游 SaaS 时使用 SCIM。一套企业身份系统中,这几类协议经常同时存在,因为它们解决的问题不同。

二、OAuth 2.0#

OAuth 2.0 解决的是授权,不定义用户登录。假设一个打印应用需要读取网盘文件,用户不必把网盘密码交给打印应用,只需要同意它读取指定文件。授权服务器随后向打印应用签发 Access Token(访问令牌)。这个令牌表示客户端可以按照指定范围访问某个 API,但它本身不负责证明当前用户是谁。

Access Token 可以是不可解析的随机字符串,也可以是 JWT。客户端通常只负责携带它,资源服务器负责验证令牌是否有效、是否发给自己,以及它包含的权限范围是否允许当前操作。

OAuth 2.0 中有四类角色。

  • Resource Owner(资源所有者),通常是用户
  • Client(客户端),希望访问资源的应用
  • Authorization Server(授权服务器),处理授权并签发令牌
  • Resource Server(资源服务器),提供受保护的 API

演示环境中,授权服务器和资源服务器可能运行在同一进程里。系统拆开后,必须分清谁负责签发令牌,谁负责验证令牌。

浏览器或移动应用中有用户参与时,通常使用 Authorization Code(授权码)流程,并配合 PKCE。公共客户端必须使用 PKCE,机密客户端也建议使用。两个后台服务以应用自身身份互相调用时,可以使用 Client Credentials(客户端凭证)。只有授权服务器签发了 Refresh Token(刷新令牌),客户端才能用它换取新的 Access Token。

Implicit(隐式模式)会让 Access Token 出现在浏览器的授权响应中,容易造成令牌泄露或重放,当前安全建议是不再使用。Resource Owner Password Credentials(密码模式)要求用户把密码交给客户端,现行 OAuth 安全最佳实践明确要求不得使用。

用户同意授权后,浏览器会跳转到类似下面的地址。

GET /authorize
  ?response_type=code
  &client_id=app-a
  &redirect_uri=https://app-a.example/callback
  &scope=repo
  &code_challenge=...
  &state=...
http

应用拿到 code 后,再从后端调用 Token Endpoint 换取 Access Token。标准 OAuth 2.0 流程不会因为用户完成授权就自动返回 ID Token。没有 ID Token,应用只能确认自己获得了访问仓库的权限,不能把 Access Token 当作用户身份凭证。

sequenceDiagram actor User as 用户 participant Client as Client participant AS as Authorization Server participant RS as Resource Server User->>Client: 请求读取仓库 Client->>User: 跳转到 /authorize<br/>携带 code_challenge User->>AS: 登录并同意授权 AS->>User: 跳回应用并携带 code User->>Client: callback?code=... Client->>AS: POST /token<br/>code + code_verifier AS-->>Client: Access Token Client->>RS: 携带 Access Token 调用 API RS-->>Client: 返回资源

三、OIDC#

OAuth 2.0 不提供标准的用户身份信息。OIDC(OpenID Connect)在 OAuth 2.0 之上增加了一层身份认证,并引入 ID Token。ID Token 是 JWT,包含授权服务器对本次用户认证作出的声明。

客户端不能只是解码 JWT、读出姓名后就相信它。至少要验证签名、issaudexp,并按所用流程检查 nonce 等字段。sub 是用户在当前签发方下的稳定标识,通常比邮箱更适合当作账号关联依据。邮箱可能变化,也可能在不同租户中重复。

OIDC 中常见的三类令牌用途不同。

  • ID Token 交给客户端,用来确认用户身份和本次认证结果
  • Access Token 交给资源服务器,用来判断 API 是否允许访问
  • Refresh Token 交给授权服务器的 Token Endpoint,用来申请新的 Access Token

不是每次 OIDC 登录都会签发 Refresh Token。客户端必须请求并满足服务端规定的条件,授权服务器才会返回它。

OIDC 请求必须包含 openid scope。需要更多用户资料时,可以增加 profileemail 等 scope,或者携带 Access Token 调用 UserInfo Endpoint。支持标准 OIDC 的“使用 Google 登录”和“使用 Microsoft 登录”,采用的就是这类流程。对于新建的 Web 或移动应用,如果双方都支持 OIDC,通常没有必要再从 SAML 开始。

完成 OIDC 登录后,应用一般还会建立自己的本地 Session。IdP 签发的 Access Token 也不会自动被企业内所有 API 接受。只有令牌的受众、签名、权限范围和其他校验条件都符合要求,某个 API 才能使用这枚令牌。

sequenceDiagram actor User as 用户 participant App as App participant IdP as OpenID Provider participant API as API User->>App: 使用企业账号登录 App->>User: 跳转到 /authorize<br/>scope=openid User->>IdP: 登录 IdP->>User: 跳回应用并携带 code User->>App: callback?code=... App->>IdP: POST /token IdP-->>App: ID Token + Access Token App->>App: 验证 ID Token 并建立本地 Session opt 需要更多用户资料 App->>IdP: UserInfo + Access Token IdP-->>App: 返回姓名和邮箱 end App->>API: 携带 Access Token 调用 API API-->>App: 返回数据

四、SAML#

SAML 2.0 比 OIDC 更早出现,也不依赖 OAuth。IdP 生成 XML 格式的 SAML Assertion,SP(Service Provider,服务提供方)验证后建立本地 Session。Assertion 可以包含用户标识、认证时间、认证方式,以及工号、邮箱、部门等属性。

下面是常见的 SP 发起登录流程。用户打开业务系统后,SP 发现本地没有 Session,于是通过浏览器把 AuthnRequest 发送给 IdP。IdP 完成认证后,通常让浏览器以 HTTP POST 的方式把 SAML Response 送到 SP 的 ACS(Assertion Consumer Service,断言接收服务)。SP 验证通过后,才允许用户进入系统。

sequenceDiagram actor User as 用户 participant SP as SP participant IdP as IdP User->>SP: 打开 OA SP->>SP: 没有本地 Session SP->>User: 跳转到 IdP<br/>携带 AuthnRequest User->>IdP: 登录 IdP->>User: 通过浏览器回传 SAML Response User->>SP: ACS 接收 SAML Response SP->>SP: 验证 Assertion 并建立本地 Session SP-->>User: 进入 OA

SAML 的安全校验不只是验证 XML 中有一个用户名。SP 还要检查签名、签发方、受众、有效时间、接收地址,以及响应是否对应自己发出的请求。否则,即使 Assertion 格式正确,也可能被错误接收或重复使用。

SAML 在存量企业软件中仍然常见。许多 SaaS 已经提供成熟的 SAML 接口,但 XML、证书和 Metadata 的配置通常比 OIDC 繁琐。新系统可以优先考虑 OIDC。对方只支持 SAML 时,就按 SAML 接入,不必为了统一协议改造整个系统。

五、CAS#

CAS 最早广泛用于高校,国内不少学校和内网门户至今仍在使用。它的核心凭证不是 JWT,也不是 SAML Assertion,而是 Ticket(票据)。

用户在 CAS 登录成功后,CAS 服务端创建 TGT(Ticket-Granting Ticket,登录票据),浏览器保存与这次单点登录会话关联的 TGC(Ticket-Granting Cookie)。访问系统 A 时,CAS 为系统 A 签发一次性的 ST(Service Ticket,服务票据)。系统 A 不自行解析 ST,而是从后端调用 CAS 的校验接口。ST 只能由指定服务使用,并且只能校验一次。

用户随后访问系统 B 时,浏览器会再次跳转到 CAS。由于 TGC 仍然有效,CAS 不再要求用户输入密码,而是为系统 B 签发另一张 ST。这就实现了浏览器中的单点登录。

sequenceDiagram actor User as 用户 participant AppA as 业务系统 A participant CAS as CAS participant AppB as 业务系统 B User->>AppA: 打开系统 A AppA->>User: 跳转到 CAS /login User->>CAS: 输入账号密码 CAS->>CAS: 创建 TGT 并写入 TGC CAS->>User: 跳回系统 A 并携带 ST User->>AppA: ?ticket=ST AppA->>CAS: 从后端校验 ST CAS-->>AppA: 返回用户身份 AppA-->>User: 进入系统 A User->>AppB: 打开系统 B AppB->>User: 跳转到 CAS /login User->>CAS: 浏览器携带 TGC CAS->>CAS: TGT 有效,无需重新登录 CAS->>User: 跳回系统 B 并携带新的 ST User->>AppB: ?ticket=ST AppB->>CAS: 从后端校验 ST CAS-->>AppB: 返回用户身份 AppB-->>User: 进入系统 B

CAS 把登录状态保留在中心,业务系统只负责校验票据。它很适合传统浏览器门户,但扩展到移动应用和 API 时,通常不如 OIDC 与 OAuth 2.0 自然。

还要区分 CAS 协议和 CAS 服务端软件。CAS 协议描述的是基于票据的单点登录。Apereo CAS 这类产品还可以支持 SAML、OIDC 和 OAuth。对接前要先确认对方要求的是 CAS 协议,还是使用某款 CAS 产品提供的其他协议。已有系统可以继续使用 CAS,新建的互联网应用和 API 则更适合采用 OIDC 与 OAuth 2.0。

六、SCIM 2.0#

SAML、OIDC 和 CAS 通常在用户登录时工作。SCIM(System for Cross-domain Identity Management)在后台同步账号和用户组。

IAM 或 IdP 中负责账号同步的组件充当 SCIM Client,下游应用充当 SCIM Service Provider。下游应用提供 /Users/Groups 等接口,双方使用 JSON 创建和更新数据。员工离职时,通常把用户资源的 active 改为 false,以便保留历史记录和引用关系,而不是直接删除账号。

POST /scim/v2/Users
Content-Type: application/scim+json

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "zhangsan",
  "emails": [{ "value": "zhangsan@example.com", "primary": true }],
  "active": true
}
http

SCIM 负责把账号状态传给目标系统,但它不规定业务系统必须怎样处理已有 Session 和 Token。目标系统收到停用状态后,还要按照自身的安全策略关闭登录能力,并撤销仍然有效的会话或令牌。

没有 SCIM 时,企业往往依赖管理员手工开账号、定时发送表格,或者让每个系统单独开发同步程序。人员规模扩大后,最常见的风险未必是登录协议失效,而是离职账号没有及时关闭。因此,企业客户接入 SSO 后,通常还会继续要求账号自动创建、更新和停用。

sequenceDiagram participant HR as HR 系统 participant IAM as IAM 或 IdP participant App as 业务系统 HR->>IAM: 入职,写入人员信息 IAM->>App: POST /scim/v2/Users App-->>IAM: 201 Created Note over HR,App: 用户之后通过 SAML 或 OIDC 登录 HR->>IAM: 离职 IAM->>App: PATCH /Users<br/>active=false App->>App: 停用账号并撤销会话 App-->>IAM: 200 OK

SCIM 不负责登录。账号同步成功后,用户仍然要通过 SAML、OIDC 或其他认证方式进入系统。

七、这些协议怎样配合#

要解决的问题常用协议
应用代用户访问 API,或者服务之间调用OAuth 2.0
新建 Web 或移动应用的登录与单点登录OIDC
对接只支持 XML Assertion 的企业软件SAML 2.0
接入已有的 CAS 门户和高校统一认证CAS
入职、调岗和离职时自动同步账号SCIM 2.0

一种常见的企业架构是,HR 系统保存员工的在职状态,IAM 把人员信息同步到目录和下游应用,IdP 同时提供 OIDC 与 SAML。已有 CAS 门户可以继续保留。新应用使用 OIDC 登录,API 使用 OAuth 2.0,SaaS 账号则通过 SCIM 创建和停用。

如果用户通过 SAML 登录后,还要访问只接受 OAuth Access Token 的 API,不能直接把 SAML Assertion 当作 Access Token。只有授权服务器或网关明确支持 OAuth Token Exchange 等转换机制时,才能用 SAML Assertion 换取 Access Token。接入双方需要事先约定令牌类型、受众、权限范围和信任关系。

判断应该使用哪种协议时,可以先问三个问题。现在要解决的是用户登录、API 授权,还是账号生命周期?系统之间传递的是认证结果、访问令牌,还是用户资料?目标系统已经支持哪些标准?把这三个问题分开,协议之间的关系就清楚了。

八、参考资料#

一文理清 IAM、OAuth 2.0、OIDC、SAML、CAS 和 SCIM
作者 Midnight
发布于 2026年9月2日