2026-06-20 · architecture / security
【身份与访问控制工程】自建还是采购:Keycloak、Auth0、Entra、Okta 对比
自建 Keycloak 省下的 license 费用,会在运维、高可用、多活、定制开发和知识积累上还回去。采购 Auth0/Okta/Entra 省下的运维成本,会在 license 账单、供应商锁定和功能黑盒上付出代价。本文不是产品推荐,而是一个工程决策框架——在什么规模、什么场景下,哪种选择的总成本(TCO)合理。














SSO 解决了”用户怎么登录”,但没有解决”用户怎么出现在系统里”。一个 500 人的公司,新员工入职第一天就需要访问 15 个 SaaS 工具——IT 部门手动在每个系统里创建账号、配权限,这个过程可能持续到入职后 3-5 天。而当员工离职时,如果 IT 遗漏了任何一个系统的账号注销,登录权限就会在离职后继续存在——这是安全审计的噩梦。
SCIM(System for Cross-domain Identity Management,跨域身份管理系统,RFC 7643/7644)就是为了自动化这件事而设计的。它定义了从企业 IdP 到 SaaS SP 之间账号(User)和群组(Group)的标准化 CRUD 接口。
flowchart LR
A["入职 (JML-Joiner)"] --> B["变岗 (Mover)"]
B --> C["休假/冻结 (Suspension)"]
C --> D["离职 (Leaver)"]
D -.-> A
| 事件 | SCIM 操作 | 业务语义 |
|---|---|---|
| 入职(Joiner) | POST /Users |
在 SP 创建用户账号,分配初始权限 |
| 变岗(Mover) | PATCH /Users/{id} 或
PUT /Users/{id} |
更新用户的部门、职位、关联群组 |
| 暂停(Suspension) | PATCH /Users/{id}
(active: false) |
临时冻结账号(如产假、出差、安全事件) |
| 离职(Leaver) | PATCH /Users/{id}
(active: false) 或
DELETE /Users/{id} |
注销账号,移除所有访问权限 |
SCIM 2.0 定义了两类 HTTP REST 端点:
/Users:用户资源的 CRUD/Groups:群组资源的 CRUD一个标准的 SCIM User 资源(RFC 7643, Section 4.1):
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "2819c223-7f76-453a-919d-413861904646",
"externalId": "employee-12345",
"userName": "zhangsan@company.com",
"name": {
"familyName": "Zhang",
"givenName": "San",
"formatted": "San Zhang"
},
"displayName": "San Zhang",
"emails": [
{"value": "zhangsan@company.com", "type": "work", "primary": true}
],
"active": true,
"groups": [
{"value": "e9e30dba-f08f-4109-8486-d5c6a331660a", "$ref": "/Groups/e9e30dba", "display": "Engineering"}
],
"meta": {
"resourceType": "User",
"created": "2026-01-15T08:30:00Z",
"lastModified": "2026-06-14T10:00:00Z",
"version": "W\/\"f250dd84f067\"",
"location": "https://sp.example.com/scim/v2/Users/2819c223"
}
}关键字段:
id:SP 内部生成的唯一标识(不可变)。externalId:IdP 侧的标识——通常是 HR
系统的员工号或 AD 的 objectGUID。这是连接 IdP 和 SP
用户的关键桥梁。userName:登录名,在 SP 中唯一。active:账号是否激活。false
表示禁用但不是删除。meta.version:ETag,用于乐观并发控制。schemas:指示这个资源顺应哪些 schema——核心
schema + 可选的企业扩展。SCIM 的 PATCH /Users/{id}
支持三种操作类型(RFC
7644, Section 3.5.2):
| 操作 | 含义 |
|---|---|
add |
添加属性或成员 |
replace |
替换属性值(整个属性级别,不是部分修改) |
remove |
删除属性或成员 |
一个典型的”变岗”PATCH 请求:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "title",
"value": "Senior Engineer"
},
{
"op": "replace",
"path": "department",
"value": "Platform Engineering"
},
{
"op": "add",
"path": "groups",
"value": [
{"value": "groupId-ops", "display": "Operations"}
]
},
{
"op": "remove",
"path": "groups",
"value": [
{"value": "groupId-intern", "display": "Interns"}
]
}
]
}工程陷阱 1:
replace操作是属性级别的整体替换,不是 JSON Patch 的”修改某个嵌套字段”。如果你只想改name.givenName,需要发送"path": "name.givenName"——SCIM 支持用点号路径访问子属性。如果直接"path": "name",会替换整个name对象。
SCIM 使用
ETag(meta.version)做乐观并发控制(RFC
7644, Section 3.13):
meta.version 作为
ETag。If-Match Header
中传递当前 ETag。409 Conflict。这防止了多个 IdP 管理员同时修改同一个用户导致的丢失更新。
SCIM 定义了两种数据同步模式:
IdP 定期拉取 SP 的全部用户列表,与自己侧的数据做 Diff,然后推送变更。问题很明显:一个 10000 人的公司,每次全量拉取传输几 MB 数据,与 100 个 SP 对接,轮询周期导致变更延迟——效率低下。
IdP
在每次同步后记住上次同步的时间点,下次只拉取该时间点之后变更过的资源。SCIM
的
GET /Users?filter=meta.lastModified gt "2026-06-14T08:00:00Z"
实现。
工程陷阱 2:SCIM 的 filter 语法(RFC 7644, Section 3.4.2.2)不是 SQL,不要被
filter=参数名骗了。它只支持有限的操作符:eq、ne、co、sw、ew、pr、gt、ge、lt、le,以及and/or。不支持嵌套逻辑、不支持 JOIN、不支持NOT。复杂的筛选逻辑需要 SP 自己扩展或在应用层做二次过滤。
在企业 SCIM 系统中,实际的同步模式是两个层次:
Azure AD 的 SCIM 实现要求 SP 实现特定的端点发现路径:
/scim/v2/Users/scim/v2/Groups/scim/v2/Schemas/scim/v2/ServiceProviderConfigAzure AD 默认每 40 分钟执行一次同步周期。对于紧急变更(如离职注销),管理员可以在 Azure AD Portal 手动触发”Provision on Demand”。
Azure AD 特有的坑: - 默认不推送群组成员关系——需要在
Provisioning 配置中显式启用 “Group Provisioning”。 - 对
DELETE 操作的支持有限——Azure AD 倾向于用
PATCH active: false
软删除,而不是物理删除。
Okta 的 SCIM 实现支持更灵活的属性映射——管理员可以在 Okta Admin Console 中自定义 Okta 用户属性到 SCIM Schema 属性之间的映射。
Okta 特有的坑: - Okta 的 SCIM 集成模板分为 “SCIM 2.0
Template” 和 “SCIM 1.1 Template”——确认客户用哪个版本。 -
Okta 的 import 功能(从 SP 拉取用户到
Okta)通常需要 SP 实现完整的 /Users GET
和分页逻辑。
SCIM 2.0 核心 schema 的 User 属性非常基础。RFC 7643
定义了企业用户扩展
schema(urn:ietf:params:scim:schemas:extension:enterprise:2.0:User):
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"employeeNumber": "EMP-12345",
"costCenter": "CC-BJ-ENG",
"organization": "Platform Engineering",
"division": "Infrastructure",
"department": "Security",
"manager": {
"value": "26118915-6090-4610-87e4-49d8ca9f808d",
"$ref": "/Users/26118915",
"displayName": "Li Si"
}
}
}如果你的 SP 需要特定的业务属性(如”来自中国团队”“有 AWS 管理员角色”),SCIM 允许自定义 schema 扩展,但需要和 IdP 确认对方是否支持把这些属性写入对应的 SCIM 字段。常见的情况是:SP 的自定义 schema 很丰富,但客户的 IdP(尤其是 Azure AD 和 Okta 的默认配置)只填充核心字段——你需要和客户对齐”哪些字段是一定会推送过来的”。
SCIM 是 SSO 的”另一半”——SSO 解决”怎么进来”,SCIM 解决”谁能进来”。对于要做企业级 SaaS 的团队:
/Users CRUD + PATCH +
filter(至少支持 userName 和
externalId 的 eq 过滤器)。meta.lastModified
的过滤)和分页(避免一次返回所有用户)。userName
的来源(email 还是 AD UPN)、externalId
的值(员工号还是 objectGUID)、企业扩展 schema
中哪些字段会推送。active: false 软删除——Azure
AD 和 Okta 都倾向这种模式。上一篇:SAML 还值得学吗:企业遗留 SSO 的现实世界 下一篇:JWT、JWS、JWE、JWKS 一次讲透
把当前热点继续串成多页阅读,而不是停在单篇消费。
2026-06-20 · architecture / security
自建 Keycloak 省下的 license 费用,会在运维、高可用、多活、定制开发和知识积累上还回去。采购 Auth0/Okta/Entra 省下的运维成本,会在 license 账单、供应商锁定和功能黑盒上付出代价。本文不是产品推荐,而是一个工程决策框架——在什么规模、什么场景下,哪种选择的总成本(TCO)合理。
2026-06-13 · architecture / security
从 2020 年 SolarWinds 到 2024 年 Okta 支持系统泄露,身份基础设施的安全失败反复证明一件事:IAM 不是 IT 支撑系统,而是安全架构的承重墙。本文建立现代 IAM 的全景地图——从认证协议、令牌体系、权限模型到身份治理与平台选型,给出 5 个贯穿全系列的核心问题。
2026-04-21 · architecture / security
从 OIDC、OAuth 2.1、SAML、SCIM 到多租户权限、CIAM、PAM 与身份平台选型——系统拆解现代身份与访问控制的协议、架构与工程实践。
2026-06-13 · architecture / security
OIDC 是当下企业 SSO 的事实标准,但大多数实现只用了它 20% 的规范。本文从 OIDC 核心规范出发,拆解 Authorization Code Flow + PKCE 的完整交互、ID Token 的验证规则、Discovery 与 Dynamic Registration 的互操作性机制,以及 RP-Initiated Logout 和 Session Management 的工程实现细节。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。