亚马逊AWS官方博客
中国区 IAM Identity Center 做 ABAC 实战:从身份属性到 PrincipalTag 的设计与取舍
摘要:随着企业把云上登录统一收敛到 IAM Identity Center,一个现实问题随之浮现:原先依赖身份属性做精细化访问控制(ABAC)的做法,在新的托管身份体系下需要重新设计。本文从企业身份治理演进的视角出发,梳理了从传统联合登录迁移到 Identity Center 时权限模型的变化,并在 AWS 中国区完成了完整验证。文章不停留在单点配置,而是提供一套可复用的判断框架:不同复杂度的场景该选择哪种方式、设计时的核心原则,以及方法本身的能力边界与常见误区。希望帮助正在推进身份统一的企业,在保证安全隔离的前提下平稳完成迁移,少走弯路。
目录
1. 概述
1.1 背景需求
越来越多客户把 AWS 的登录方式从”外部身份提供商直连 IAM SAML Provider、再 assume 预建 IAM 角色”迁移到 IAM Identity Center(以下简称 IDC),动机通常有几点:
- 统一的登录门户和用户体验
- 集中管理多账号、多权限集的分配
- 通过临时 AKSK 使用 CLI
- 更完整、更一致的审计
AWS 有一篇很好的英文博客,讲了怎么用 Microsoft Entra ID 的自定义属性配合 IDC 做基于属性的访问控制(ABAC):Use custom attributes for ABAC with Microsoft Entra ID and AWS IAM Identity Center。基本机制那篇讲清楚了,但真实迁移里有几个问题它没展开,而这些往往是落地时最容易卡住的地方:标签怎么放进会话、claim 怎么设计、aws:PrincipalTag 怎么用,以及有哪种场景这套方法覆盖不了。
1.2 方案介绍
本文是那篇英文博客的进阶补充。我们在 AWS 中国区(aws-cn)完整验证了这套方案,用一个假设的场景把上面几个问题讲透,最后单独讨论一种覆盖不了的情况。文中不涉及任何真实客户信息,例子均为示意。
2. 迁移打断了什么
先用一个假设的场景把问题摆出来。假设某客户想按应用来控制资源访问:负责 app1 的人能操作 app1 的资源(比如各应用专属的 KMS 密钥、Secrets),负责 app2 的人操作 app2 的资源,以此类推。
在传统的 SAML + IAM 角色方式下,常见做法是这样:
- 为每个应用建一个 IAM 角色,并给角色打上应用标签,例如
Role-app1带app=app1; - 给每个应用的资源也打上对应标签,例如 app1 的密钥带
app=app1; - 在资源策略里写一条动态匹配,要求访问者的
app标签等于资源的app标签。
用户 SAML 登录后 assume 进 Role-app1,角色自身的标签就成了会话的 aws:PrincipalTag/app=app1;再去访问 app1 的资源,资源标签也是 app1,两边相等,于是放行。这里有两个前提缺一不可:发起方的会话带着 app 标签,目标资源也带着同名的 app 标签,策略比的就是这两者相不相等。
迁到 IDC 后,这条链断在第一环:登录拿到的是 AWSReservedSSO_* 服务托管角色,AWS 不允许给它打标签,权限集上的标签也不会传播过去。”角色自身标签变成 principal tag”这个来源没有了,app 标签只能改由会话来携带。
碰到这一点,不必急着把标签补回来。既然登录后落在固定的权限集角色上,最简单的做法是不依赖标签,直接在策略里认这个角色,用 aws:PrincipalArn 匹配权限集角色的 ARN 就行。比如只放行 app1 权限集的会话访问 app1 的资源:
清楚、可靠,对不少客户这就够用了。
但有些客户想要更灵活:策略不愿和某个具体角色写死,而是希望”带了什么属性就能访问对应的资源”,也就是继续走 ABAC、继续用标签判断。于是问题回到:IDC 的会话默认不带业务标签,怎么把 app 这样的标签放进会话?
3. 把标签放进 IDC 会话:属性映射,还是 SAML claim
给 IDC 会话带上业务标签,有两种方式,来源和灵活度不同。
方式一:用 IDC 的 Attributes for access control 做属性映射。 IDC 能把身份源里已有的用户属性映射成会话标签。用外部 IdP(比如 Microsoft Entra ID,即原 Azure AD)时,这些属性通过 SCIM 同步进 IDC 目录,您在 IDC 控制台的 Attributes for access control 页面配一条映射。
举个具体例子:先在 Entra ID 里把某用户的 department 设成 app1,SCIM 同步进 IDC 后,在 Attributes for access control 里加一条 Key = app、Value = ${path:enterprise.department};这样该用户登录时,IDC 就把 department 的值 app1 作为 aws:PrincipalTag/app=app1 带进会话。
限制在于,这里的 Value 只能从一组固定的标准属性里选,用不了自定义或扩展属性。按 AWS 官方文档,外部 IdP 能映射的属性就是下面这些(这是完整列表):
| 类别 | 可用属性(节选自完整列表) |
| 姓名 / 账号 | ${path:userName}、${path:displayName}、${path:nickName}、${path:name.givenName}、${path:name.familyName} |
| 联系方式 | ${path:emails[primary eq true].value}、${path:phoneNumbers[type eq "work"].value}、${path:addresses[type eq "work"].locality}(以及 streetAddress / region / postalCode / country / formatted) |
| 职务 / 区域 | ${path:title}、${path:userType}、${path:locale}、${path:timezone} |
| 企业属性 | ${path:enterprise.department}、${path:enterprise.costCenter}、${path:enterprise.division}、${path:enterprise.organization}、${path:enterprise.employeeNumber}、${path:enterprise.manager.value} |
完整列表见官方文档 Attribute mappings between IAM Identity Center and External Identity Providers 的 “Supported external identity provider attributes” 一节。可以看到,这些全是标准字段,没有一个自定义槽位。
问题就出在这里。想承载”这个用户负责哪个应用”这种业务值,只能挑上面某个标准字段来塞(比如把应用名写进 department),这就得去改比如 Entra ID 里用户对象的这个属性。很多客户的 Entra ID 是集中管理的,department 这类字段要么已被占用,要么不允许随意改。本文假设的客户正是卡在这里,才转向下面的方式二。
方式二:用 SAML claim 追加一个自定义标签。 在 Entra 企业应用里直接定义一条 AccessControl:<key> 的 claim,用组条件下发值(具体怎么配见下一节)。它不依赖 SCIM 同步的标准字段,也不用改 Entra ID 用户对象的属性,key 和值都在企业应用里自定义,还能按组动态赋值,灵活度最高。
两者怎么选:业务值能放进一个受支持的标准属性,就用方式一,最省事;字段受限、或不方便改 Entra ID 用户属性(这是更常见的情况),就用方式二的 SAML claim。本文后面重点讲方式二。
4. 方式二详解:SAML claim 的机制与中国区验证
方式二的机制就是那篇英文博客讲的,并不复杂:在 Entra ID 的 SAML 断言里,用一个自定义属性承载业务值,属性名写成 https://aws.amazon.com/SAML/Attributes/AccessControl:<TagKey>;IDC 打开 Attributes for access control 之后,会把这类属性透传成会话标签 aws:PrincipalTag/<TagKey>;之后就能在权限集、资源策略或信任策略里用它做 ABAC。
有个细节要注意:通过外部 IdP 的 SAML 断言传进来的属性,不需要在 IDC 的 Attributes for access control 页面再对同名 key 做映射;如果重复映射并填了固定值,IDC 目录里的值反而会覆盖 SAML 传来的值。
在中国区跑这套方案,机制和全球区一致,只有端点和分区不同:
- SAML 断言的 Audience 和 ACS 指向中国区 sign-in 端点(
*.signin.amazonaws.cn) - 策略 ARN 使用
aws-cn分区 - 属性名前缀仍然是
https://aws.amazon.com/SAML/Attributes/AccessControl:,用的是aws.amazon.com而不是amazonaws.cn。这是 SAML 属性的命名约定,和分区无关
4.1 配置流程走一遍(中国区)
英文博客把每一步讲得很细,这里不重复,只把在中国区跑通的关键几步串一遍,并在每处留一张截图,证明中国区对接成功。
第一步:在 Entra 企业应用里配 claim。 进入对应 IDC 的企业应用 → Single sign-on → Attributes & Claims,新增一条 claim:Name 填 AccessControl:app,Namespace 填 https://aws.amazon.com/SAML/Attributes,再用 Claim conditions 按组下发值(属于 App1-Users 就下发 app1)。
[图 1 – 在 Entra 企业应用配置 AccessControl:app claim(namespace 与组条件)] |
第二步:把组分配给这个企业应用。 Entra → 企业应用 → Users and groups,把 App1-Users 等组加进来。组条件 claim 只对分配给应用、且是直接成员的用户生效。
[图 2 – 将相关用户组分配给该企业应用] |
第三步:在 IDC 启用 Attributes for access control(中国区控制台)。 IDC 控制台 → Settings → Attributes for access control → 启用。SAML 透传的属性无需在这里重复映射同名 key,保持空表即可。
启用前
[图 3] |
启用后
[图 4 – 在中国区 IDC 控制台启用 Attributes for access control(启用前后对比)] |
第四步:用测试用户登录中国区 access portal。 打开中国区的 AWS access portal,用测试用户登录,进入分配好的账号和权限集。
[图 5 – 中国区 AWS access portal 登录后的账号与权限集] |
[图 6 – 中国区 AWS access portal 登录后的账号与权限集] |
第五步:验证标签确实进了会话。 这一步最关键。登录后到 CloudTrail 查这次登录的 AssumeRoleWithSAML 事件,requestParameters.principalTags 里应当出现 app(或 app1=true 等)自定义标签。看到它,就说明中国区这条链路整个打通了。
[图 7 – CloudTrail 的 AssumeRoleWithSAML 事件中出现自定义 principalTags] |
(可选)再用这个带标签的会话去访问一个受 ABAC 保护的资源,验证放行/拒绝符合预期。
被访问资源,携带 app=app1 的 tag:
[图 8] |
测试策略:
[图 9] |
[图 10 – 用带 app 标签的会话访问受保护资源,结果符合预期] |
5. Claim 怎么配:一条 claim 只会产出一个值
要把 app 带进会话,就在 Entra 的 Attributes & Claims 里加一条 AccessControl claim。怎么配才对,得先弄清一条 claim 到底会产生什么,再一步步往下推。
第一步:一条 claim = SAML 里一个属性 = 一个值。 登录时,您加的这条 claim 会在 SAML 断言里变成一个 <Attribute>,里面只有一个 <AttributeValue>。Entra 不能让一条自定义 claim 带多个值(官方明确:一个 claim 不能是数组/多值)。IDC 收到后,把 AccessControl:app 这条 claim 转成会话标签 aws:PrincipalTag / app,值就是那一个 <AttributeValue>。这条是后面所有取舍的根:一条 claim 只回答一个值。
第二步:让这个值随”用户在哪个组”而变,用 claim 的条件。 光有一条 claim 不够,您要的是”属于 app1 组的人带 app1,属于 app2 组的人带 app2″。这靠 claim 下面的条件(Claim conditions)实现,每一行条件是”如果用户是某个组的成员,这条 claim 就取某个值”:
于是属于 App1-Users 的人登录,这条 claim 就带 app1。
第三步:一个人同时在多个组时,只会取到一个值。 如果有人既在 App1-Users 又在 App2-Users 呢?Microsoft 官方的行为是:条件从上到下评估,最后一个匹配的值被写进 claim(Entra 官方文档:Emit claims based on conditions)。所以不管命中几个条件,这条 claim 最终只带一个值(排在后面的那个),不会把 app1、app2 一起带出来。换句话说,一条 claim 无论配多少条件,产出永远是单值;条件只是帮您在多个候选里挑出那一个,不是把多个拼起来。
第四步:按”一个用户是不是只对应一个值”分成两种配法。
配法一是一条 claim、单值,用在”一个用户只对应一个值”的情况(比如每人只负责一个应用):就是上面第二步那样,一条 AccessControl:app,用条件把每个组映射到一个值。张三属于 App1-Users,登录后 SAML 里是:
进到会话就是 aws:PrincipalTag/app = app1,策略直接比这个值。
配法二是每个值一条 claim、值恒为 true,用在”一个用户可能同时对应多个值”的情况(比如一个人同时负责 app1、app2、app3):一条 claim 装不下多个,所以给每个应用各建一条独立的 claim,值写死成 true,各自带自己那个组的条件:
这里每一条 claim 还是单值(值就是 true),但因为是多条独立的 claim,用户在几个应用组里就命中几条,拿到几个标签。这一步是跟着组动态的:3 个组的人拿 3 个,5 个组的人拿 5 个。张三在 App1-Users、App2-Users 两个组,登录后 SAML 里是两条:
进到会话就是两个标签:aws:PrincipalTag/app1 = true、aws:PrincipalTag/app2 = true。
两种配法的分界在于信息放在哪:配法一把信息放在值里,一个 key 换值,简单,但一个用户只能有一个值;配法二把信息放在 key 里,一个 key 对应一个成员、值恒为 true,能表达一个人的多重身份,代价是 claim 数量多、要有命名规范。
6. 策略怎么用这两种标签
标签进了会话,策略侧就照着上面两种形态来写。
配法一是单值 app,比的是值。最常见的是让访问者的 app 等于资源上的 app 标签,一条策略通吃所有资源:
如果只是想让某个资源对固定几个应用开放,用数组就是”或”:
配法二是多个 appX=true,比的是 key 在不在。每条策略只查自己关心的那个 key:app1 的资源查 aws:PrincipalTag/app1 是不是 true,app2 的查 aws:PrincipalTag/app2。一个人同时具备多个应用的资格时,各资格互不干扰。
看到这里可能会问:配法一那条”一条策略通吃”的动态匹配这么优雅,能不能也用在”一人多应用”上?不能,原因放在下一节说。
7. 一种这套方法覆盖不了的场景:资源策略的动态匹配
回到上一节那条最优雅的写法,资源策略里做值到值的动态匹配,一条策略通吃所有资源:
它能不能平滑迁到 IDC,取决于一件事:每个用户是不是只对应一个值。
如果一个用户只负责一个应用(只对应一个 app 值),那没问题:claim 按组下发这一个值,会话带上它,这条动态策略照样一条通吃,迁移是顺的,资源侧一个字都不用改。
下面这张图对比了两种用户在同一条动态策略下的结果:
[图 11 – 一人一值:命中 / 一人多值:无法表达] |
如果一个用户同时负责多个应用,就走不通了,原因有三层,层层叠加:
- 会话标签是单值的,一个
app装不下{app1, app2}这个集合。 - Entra 没法在登录时把多个组拼成一个值。一个 claim 只出一个值,命中多个组条件时只取一个,不会用分隔符拼成
app1;app2,也没有”遍历我所有组再聚合”的取值方式。想要这样一串,只能预先把它同步进用户的某个单值属性里,由外部程序维护。 - IAM 策略变量不能出现在 condition 的 key 上。所以也写不出”检查访问者有没有
<当前资源的 app>=true这个标签”这种一条通用策略,变量只能用在 condition 的值和 Resource 的 ARN 上,不能用来动态拼 key。
这三条叠加的结果是:对”一人多应用”的用户,没有任何一条通用的动态策略能覆盖。claim 和会话标签的 ABAC,只在”一个用户对应一个值”时能优雅地保留这条动态匹配;一旦一个用户跨多个值,这条路就到头了。
真要支持”一人多应用”,有几条各有取舍的替代路:
- 按角色 ARN 授权。每个应用一个权限集,资源策略从”匹配标签”改成”匹配权限集角色 ARN”(
aws:PrincipalArn通配AWSReservedSSO_PS-app1_*)。用户在门户选进哪个应用的权限集,等价于原来选哪个角色,一个会话仍然只在一个应用里,强隔离保留。代价是丢了”一条通用策略”,每个应用各写一条(可用 IaC 模板化)。 - 保留带标签的 per-app IAM 角色,IDC 只当登录入口。让 IDC 会话再切进这些普通角色,标签由角色自身携带(principal tag 的第一个来源),原来那条动态资源策略一字不改,控制台 Switch Role 也能用,因为不需要传标签。代价是继续维护这批角色,且角色链会话最长 1 小时。
- CLI/SDK 场景。登录后由用户
assume-role --tags app=app1当场把所选应用的值传进去,动态策略照样命中。代价是控制台不能传 session tag,这条只对命令行成立。
所以资源策略动态匹配这种用法本身很好,迁 IDC 后在”一人一值”时能平滑保留;到了”一人多值”,claim 覆盖不了,只能在上面几条里按访问方式权衡。
8. 排错:用 SAML-tracer 抓 SAML 断言核对属性
为什么要排错:claim 配好了、属性映射也启用了,登录后标签却没进会话,或者拿着会话去访问资源被拒,这类问题十有八九出在 SAML 断言这一环:claim 没被下发、属性名或 namespace 写得不对、又或者用户不是目标组的直接成员,IdP 压根没把这条属性发出来。麻烦的是,站在 IDC 和策略这一侧都看不出原因,只能回到源头,看 Entra 实际发出来的断言到底长什么样。
怎么排错:SAML-tracer 是一个浏览器扩展(Chrome、Firefox 都有),它能在浏览器这一端把原始 SAML 断言抓下来,也就是 IdP 到底发了什么的第一手证据,比在后面各环节反复猜要可靠得多。
具体做法是:装好扩展后先把它打开,再走一次正常登录;在抓到的请求里找 URL 形如 https://cn-north-1.signin.amazonaws.cn/platform/saml/acs/XXXXX 的那一条(这就是把断言提交给中国区 IDC 的 ACS 请求),打开它的 SAML 标签页,定位到 AttributeStatement,逐字符核对两件事:一是属性名是不是 https://aws.amazon.com/SAML/Attributes/AccessControl:app(前缀、大小写、冒号后面的 key 都要对得上),二是里面的 AttributeValue 是不是您期望的那个值。
常见的几种结论:属性根本没出现,多半是组条件没命中、或用户不是该组的直接成员;属性在、但 key 或 namespace 有一丁点出入,IDC 就不会把它透传成会话标签;值不对,就回到 claim 的条件顺序去查(记住是最后一个匹配的条件值胜出)。断言核对无误后,再回 CloudTrail 的 AssumeRoleWithSAML 事件确认 principalTags,整条链就闭环了。
[图 12 – 用 SAML-tracer 抓取并逐字符核对 SAML 断言中的 AccessControl 属性] |
9. 总结:几种方式怎么选
下面这张表按”满足哪个条件就选哪种方式”排列,从上往下读,第一个匹配您情况的那行就是推荐做法:
| 如果您的情况是…(条件) | 就用这种方式 | 关键限制 / 代价 |
| 策略可以直接写死角色,根本不需要标签 | 直接认角色 ARN(aws:PrincipalArn 匹配 AWSReservedSSO_PS-app1_*) |
最简单;策略和角色绑定,新增应用要加一条策略 |
| 要用标签(ABAC),且业务值能放进一个受支持的标准属性、也能改 Entra ID 用户属性 | 方式一:IDC 属性映射(如 app ← ${path:enterprise.department}) |
只能用固定的标准字段(见前文列表),常因改不动 Entra ID 而走不通 |
| 要用标签,但字段受限或改不动 Entra ID,且一个用户只对应一个值 | 方式二 + 单值 claim(app=app1) |
灵活、按组动态;一条动态策略可通吃所有资源 |
| 要用标签,且一个用户同时对应多个值(负责多个应用) | 方式二 + 多个 flag claim(app1=true、app2=true) |
能表达多重身份;但没有一条通用动态策略,得按 key 逐条判断 |
[图 13 – IDC 下做 ABAC 的选型决策流程] |
把这条链口述一遍:不需要标签就认角色 ARN;要标签就看业务值能不能进标准属性,能就方式一,不能就方式二;走方式二再看一个用户对应几个值,一值用单值 claim,多值用多 flag claim。
还有那条边界要记住:资源策略里”principal tag 等于 resource tag”的动态匹配虽然优雅、一条通吃,但只在一个用户对应一个值时才能被 claim 覆盖;一旦一人多值,就得改用按角色 ARN 授权、保留带标签的业务角色再切进去、或 CLI 传标签这几条替代路。
10. 结语
这套方法适合正在从传统 SAML 联合登录迁移到 IDC、又依赖 principal tag 做精细化访问控制的客户。要点其实就一条:让正确的 aws:PrincipalTag 出现在会话里。迁到 IDC 后它的来源从”角色自身标签”换成了”SAML 断言里的 AccessControl: claim”,前面讲的所有取舍,都是围绕怎么把这个标签正确地带进来、又不越过它的能力边界展开的。希望本文在中国区的验证和这些取舍,能帮您在类似场景里少走一些弯路。
➡️ 下一步行动:
相关产品:
- Amazon IAM — 身份管理和访问权限
- Amazon WAF — Web 应用程序防火墙
- Amazon CloudTrail — 审计跟踪
- Amazon Connect — AI 客户体验解决方案
- Amazon KMS — 托管式密钥管理
相关文章:
- 基于 Amazon ECS Fargate 自建 Keycloak 作为 AWS IAM Identity Center 外部 IdP,为 Kiro 提供企业级 SSO 登录
- 通过 Microsoft Entra ID 集成 IAM Identity Center 实现对 Amazon Quick 的统一身份认证
- Amazon Quick Desktop 企业 SSO 实战
11. 关键限制与坑点
下面这些是会直接影响落地成败的技术边界,配置前建议先对照一遍:
| 限制 / 边界 | 对落地的影响 |
| IDC 登录得到的是 AWSReservedSSO_* 托管角色,不能给它打标签,权限集上的标签也不会传播到会话 | 传统“角色自身标签→principal tag”这条路断了,标签只能靠 SAML claim 带进会话。 |
| 一条 claim 只产出一个值;用户命中多个组条件时,只取最后一个匹配的值 | 一条 claim 无法表达“一个人同时属于多个应用”;要表达多重身份,必须给每个值单建一条 claim(值恒为 true)。 |
| Entra 不能在登录时把多个组拼成一个值 | 没有分隔符拼接、也没有“遍历我所有组再聚合”的取值方式;想要一串值,只能预先同步进某个单值属性、由外部程序维护。 |
| IAM 策略变量不能出现在 condition 的 key 上 | 写不出“检查访问者有没有 <当前资源的 app>=true”这种一条通用策略;变量只能用于 condition 的值和 Resource 的 ARN。 |
| 会话标签是单值,装不下集合 | 一个 app 标签放不下 {app1, app2};“一人多应用”下没有任何一条通用动态策略能覆盖,只能换授权方式。 |
| 控制台会话不能传 session tag | assume-role –tags 传值只在 CLI/SDK 成立;控制台走 ABAC 动态传值这条路不通。 |
| 角色链会话最长 1 小时 | 用“保留带标签的业务角色、IDC 会话再切进去”这条替代路时,会话时长受限,长任务需评估影响。 |
| 方式一的 Value 只能选标准属性;SAML 属性前缀固定用 aws.amazon.com(与分区无关) | 承载自定义业务值必须占用某个标准字段(如 department),而它常被 HR 等用途占用或不允许改;中国区也别把前缀改成 amazonaws.cn。 |
12. 参考
- Use custom attributes for ABAC with Microsoft Entra ID and AWS IAM Identity Center
- Attributes for access control(IAM Identity Center 用户指南)
- Pass session tags in AWS STS(会话标签的单值、传递性,以及控制台不能传标签)
- Tagging IAM Identity Center resources(不能给 IDC 角色打标签)
- IAM policy elements: Variables and tags(策略变量只能用于 condition 值和 Resource)
- Customize SAML token claims(Microsoft Entra:自定义 claim 与 claim 条件,含”最后一个匹配值胜出”的行为)
- Attribute mappings between IAM Identity Center and External Identity Providers(外部 IdP 支持映射的属性完整列表)
*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。
本篇作者
AWS 架构师中心:云端创新的引领者探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用 |
![]() |














