资讯详情

资讯详情

中国区Azure AI Search非密钥认证:Entra ID RBAC配置实战与避坑指南

1. 为什么中国区 AI Search 的非密钥认证绕不开 Entra ID RBAC1.1 中国区和全球版的差异不光是端点域名换了那么简单如果你最近开始接手中国区的 Azure AI Search大概率会遇到一个有点别扭的场景代码在本地用管理密钥连得好好的换到生产环境却被安全团队卡住要求不能再用硬编码 API Key必须走 Entra ID 的 RBAC 认证。我前段时间正好把这条链路完整走了一遍从应用注册、角色分配到代码侧接入中间踩了不少坑。这篇就按实际操作的顺序把关键细节和报错排查一起整理出来。先说一个最容易被忽略的背景中国区的 Azure 是一套独立部署的云环境资源管控平面、身份认证体系都是单独维护的。这意味着你在全球版写的认证代码拿到中国区基本不能直接跑。最常见的病根有三个登录终结点不同、搜索服务域名不同、Token audience 不同。这几样东西在代码里如果只写死成全球版的常量换到中国区就会报出各种莫名其妙的 401、403 或者 audience 不匹配。很多人以为中国区和全球版的差异只是把 Endpoint 从search.azure.com换成search.azure.cn就完了其实对认证来说最关键的差异在“你找谁要 Token”。全球版走的是公开的 Entra ID 登录终结点中国区要走本地环境的登录终结点。这个终结点如果不对SDK 会拿着你给的 client id 和 secret 去错误的地址换 Token然后一路失败到最后一步报错信息还特别隐晦。1.2 API Key 能用为什么还要费劲折腾 RBAC在开讲配置之前我想先花点时间说清楚“为什么非要走 RBAC”。这不是为了给厂商背书而是实际项目里真的被逼到这一步。API Key 本身只有两档管理密钥admin key和查询密钥query key。管理密钥能读写索引也能删服务改配置权限大到离谱查询密钥只能查数据算是够用但它是一个静态字符串谁拿到谁就能用。如果你的应用部署在多台机器上或者有多个团队在共享同一个搜索服务API Key 的管理问题就会立刻暴露出来——改了一个应用的密钥其他所有应用都得跟着改不然全部断连。RBAC 认证的核心价值是把“身份”和“权限”分开。每个应用用自己独立的身份应用注册或者托管身份去申请 Token然后在 AI Search 服务上给这个身份分配具体角色。搜索数据的读、写、服务管理可以完全拆开给 A 团队只读权限、给 B 团队读写权限。就算某个团队的身份出问题把它的角色拿掉就行其他团队不受影响。用生活里的例子类比API Key 就像给每个人发了一把家门钥匙谁拿谁进丢了还得换全屋锁RBAC 像写字楼的门禁系统每个人凭自己的工牌进门HR 一注销工牌人就进不去了完全不用动其他任何人。对于有合规审计要求的项目RBAC 还有一个 API Key 永远给不了的能力——操作可追溯。哪个身份在什么时间做了什么操作在微软 Entra 的登录日志和控制台活动日志里都能查到。这一点对安全团队来说几乎是刚需。1.3 哪些场景最需要这套方案结合我实际看到的情况以下四类场景建议直接上 RBAC别在 API Key 上纠结多应用共享同一个搜索服务内部系统、门户网站、移动端共用一个 AI Search 服务各自权限不同用 API Key 只能一个读一个写没法细粒度控制。有合规审计要求的项目需要清晰记录哪个应用访问了哪些数据API Key 无法区分具体调用者只有身份认证才能做到。CI/CD 自动化部署流水线里要创建索引、更新数据源不能让部署脚本持有高权限管理密钥用服务主体身份更安全也更容易撤销。密钥轮换困难的存量系统如果你的服务端存储的密钥一旦失效会影响大量客户端不如一次性迁移到 RBAC以后只轮换身份凭据。2. 连接前必须搞懂的几个 RBAC 权限细节2.1 三个数据平面角色和一个管理角色的分工Entra ID RBAC 连接 AI Search不需要你在代码里手动写复杂策略只需要理解内置角色就行。Azure AI Search 的内置角色说多不多真正跟日常开发强相关的就这几个角色名称权限范围典型使用场景Search Service Contributor管理搜索服务本身创建/删除索引、管理数据源、技能组等运维/管理后台、部署脚本Search Index Data Contributor读写索引中的文档增删改查数据同步任务、写索引的应用Search Index Data Reader只读索引文档查询面向搜索请求的前端应用、报表查询对照这个表能发现一个有意思的点如果某个应用只做索引数据写入你给它 Search Index Data Contributor 就行它没法删除整个搜索服务如果某个应用只做查询给它 Search Index Data Reader 就够连写数据的权限都没有。这比 API Key 只有“全都能干”和“只能查”两档要精细得多。这里要特别提醒一下“数据平面”和“控制平面”的区别。在 Azure 门户的 IAM访问控制里给角色分配时你选的是一些看起来差不多的列在“内置角色”里的角色但它们对应的是数据平面的权限即直接操作索引和文档。控制平面的经典角色Owner、Contributor、Reader管的是搜索服务这个资源本身的创建、删除、配置等管理动作。连接索引来查询和写入用的是数据平面的三个搜索专用角色不要搞混。2.2 身份到底是谁应用注册、服务主体和托管身份搞懂身份模型是这整条链路里最容易绕晕的地方。在 Entra ID 里新建一个“应用注册”系统会同时创建对应的“服务主体”。应用注册有点像你要发的工牌的样子模板服务主体才是真正被挂到 AI Search IAM 下、被授权的那个人。在 RBAC 分配角色时你搜索的是一个应用注册的名称实际上匹配到的是它的服务主体。所以如果你们团队有多个应用共用一个注册那它们会共享同一个身份和权限。官方推荐的做法是一个应用一个注册不要共用否则权限不好隔离。身份获取 Token 的方式又有两类应用注册加客户端机密Client Secret适合部署在虚拟机、本地服务器、容器里的应用或者无论在哪台机器上都能运行的后端服务。最简单直接但要负责保管好 secret。托管身份Managed Identity适合部署在 Azure 内部如 App Service、Function、VM的应用由云平台自动管理身份凭据代码里完全不用写 secret。中国区同样支持系统分配和用户分配托管身份我强烈建议只要是 Azure 内部部署的组件一律优先走这个方式。2.3 记住这两个中国区专属参数这是全篇最实用的一段也是很多人卡住半天的原因。连接中国区 AI Search 时有两个参数必须盯住登录终结点Authority Host全球版默认是https://login.microsoftonline.com中国区应该设置为https://login.partner.microsoftonline.cn。SDK 里的AzureAuthorityHosts.AzureChina指的就是它。Token Audience资源 ID / scope全球版默认是https://search.azure.com/.default中国区必须写成https://search.azure.cn/.default或者在一些 SDK 里直接指定对应的SearchAudience.AzureChina。这两个参数如果不匹配最常见的结果就是你成功换到了 Token但把 Token 拿给中国的搜索服务验证时对方一看 audience 不对直接拒绝。报错可能长得很吓人比如AADSTS70011或者Invalid audience排查到最后大概率就是这里的配置问题。3. 三步完成 RBAC 认证配置并在代码中接入3.1 前置条件清单在自己动手之前先把下面的东西准备好避免配置到一半才发现缺权限一个中国区可用的订阅且已创建好 Azure AI Search 资源在门户里资源名称可能显示为“AI Search”或“认知搜索”。在 Entra ID 中具有创建应用注册的权限。如果你用的是企业环境可能还需要管理员同意相关 API 权限。已经确定要连接的索引名称和搜索服务域名形如https://你的服务名.search.azure.cn。准备好你熟悉的开发语言运行环境推荐 Python 3.8 或 .NET 6。如果代码要部署在 Azure 内部提前决定是否使用托管身份这会影响后续代码写法。3.2 第一步在 Entra ID 中创建应用注册和客户端机密打开中国区 Azure 门户进入 Entra ID原 Azure Active Directory找到“应用注册”点击“新建注册”。名称写一个能区分项目的名字例如“搜索服务数据读写应用”。不用太纠结命名规则之后会用到它的客户端 ID。支持的账户类型选“仅此组织目录中的账户”就够了我们只用它访问当前租户内的 AI Search。重定向 URI 可以留空后端应用用不到它。创建完成后马上记下两串东西应用程序客户端ID和目录租户ID后面代码里都要用。接着进入“证书和机密”新建一个客户端机密。这里要注意机密的有效期最长为两年系统只会显示一次“值”刷新页面之后就再也看不到了所以必须立刻把它保存到安全的位置。我习惯把它放到密钥管理服务里而不是明文写进配置文件因为一旦代码仓库泄露机密被拿走就等于身份被拿走。如果你打算用托管身份这一步可以跳过在 Azure 资源上开启托管身份即可。但注意托管身份的权限分配方式跟应用注册略有区别如果是用户分配托管身份本身就是独立身份如果是系统分配托管身份需要先找到对应资源实例再在搜索服务的 IAM 里给这个资源分配角色。3.3 第二步在 AI Search 服务上分配 RBAC 角色进入你的 Azure AI Search 资源页面找到“访问控制 (IAM)”点“添加”选择“添加角色分配”。角色选“Search Index Data Reader”或“Search Index Data Contributor”。如果是要做管理操作则选“Search Service Contributor”。成员类型选“用户、组或服务主体”然后在搜索框里输入刚刚创建的应用注册名称选择对应的服务主体。点击“分配”。这里有三点实操经验值得说给同一个身份可以分配多个角色。比如一个应用既要写索引数据又要在初始化时检查服务状态那就同时给 Data Contributor 和 Search Service Contributor。如果你想要更细的控制可以考虑自定义角色。但内置角色已经覆盖了 90% 的场景新手建议先用内置角色跑通。角色分配刚做完通常不会立刻生效要等 30 秒到几分钟的传播时间。别急着调试半天先去喝口水再回来看。如果你希望某个应用只读不写只给 Search Index Data Reader 即可。如果发现角色分配不生效优先在 IAM 页面确认是不是选到了同一个租户下的身份跨租户的身份在标准 RBAC 里是不被支持的。3.4 第三步Python 代码侧接入实操示例代码侧我用 Python 演示因为它的 SDK 结构最直观。先安装依赖pip install azure-search-documents11.4.0 azure-identity1.15.0下面是一段最完整的准入式连接代码包括了创建 Credential、构造 SearchClient、发起搜索请求from azure.identity import ClientSecretCredential from azure.search.documents import SearchClient tenant_id 你的目录(租户)ID client_id 你的应用程序(客户端)ID client_secret 你的客户端机密值 # 中国区搜索服务域名注意是 search.azure.cn 而不是 search.azure.com endpoint https://你的服务名.search.azure.cn index_name hotels-sample-index # 创建凭据对象使用中国区登录终结点 credential ClientSecretCredential( tenant_idtenant_id, client_idclient_id, client_secretclient_secret, authorityhttps://login.partner.microsoftonline.cn ) client SearchClient( endpointendpoint, index_nameindex_name, credentialcredential ) # 发起搜索 results client.search(search_textbeach, top5) for result in results: print(result[HotelId], result[HotelName])在这段代码里authority参数传入了中国区登录终结点这是和全球版代码的最大区别。如果 SDK 版本较新有些版本会提供AzureAuthorityHosts.AZURE_CHINA常量直接引用更安全避免手滑把字符串打错。SDK 内部在获取 Token 时会自动根据你传入的 endpoint 决定访问的 audience。如果你的 SDK 版本比较老没有自动识别.search.azure.cn你就需要手动传入audience参数比如把 scope 指定成https://search.azure.cn/.default。怎么判断要不要手动传跑一次看报错凡是出现 Invalid audience 类的提示基本就是这里的问题。如果你用的是用户分配托管身份代码稍微改一下from azure.identity import ManagedIdentityCredential credential ManagedIdentityCredential( client_id用户分配托管身份的客户端ID # 系统分配时这一行可省略 )代码结构完全一样唯一的好处是配置里不再有 secret 了。对于跑在 Azure 云资源上的服务这一步能帮你把密钥从配置里彻底清掉。3.5 .NET 代码侧接入实操示例.NET 侧的写法类似但有一个额外的坑构造函数里要显式指定 Audience。先安装包dotnet add package Azure.Search.Documents dotnet add package Azure.Identity示例代码using Azure; using Azure.Identity; using Azure.Search.Documents; string tenantId 你的目录(租户)ID; string clientId 你的应用程序(客户端)ID; string clientSecret 你的客户端机密值; var credential new ClientSecretCredential( tenantId, clientId, clientSecret, new ClientSecretCredentialOptions { AuthorityHost AzureAuthorityHosts.AzureChina }); string endpoint https://你的服务名.search.azure.cn; string indexName hotels-sample-index; var searchClient new SearchClient( new Uri(endpoint), indexName, credential, new SearchClientOptions { Audience SearchAudience.AzureChina }); SearchResultsDictionarystring, object response searchClient.SearchDictionarystring, object(beach); foreach (SearchResultDictionarystring, object result in response.GetResults()) { Console.WriteLine(result.Document[HotelName]); }注意SearchClientOptions.Audience SearchAudience.AzureChina这个参数在 Python SDK 里一般是自动推导的但在 .NET SDK 的某些版本里不会自动为中国区 endpoint 切换 Audience。如果你直接拿全球版的代码改成中国区 endpoint不设置 Audience就会报一个很典型的 The audience ... is invalid 异常。我在第一次跑这个示例时没有显式设置它结果调试了足足一个下午。3.6 如果不想用 SDK手动获取 Token 该怎么写有时候你需要在原始 HTTP 请求里加上 Bearer Token比如调用自定义技能或者自己封装 REST API。这种情况下可以手动从 Entra ID 换 Token关键是 scope 要写对from azure.identity import ClientSecretCredential credential ClientSecretCredential( tenant_idtenant_id, client_idclient_id, client_secretclient_secret, authorityhttps://login.partner.microsoftonline.cn ) token credential.get_token(https://search.azure.cn/.default) headers { Authorization: fBearer {token.token}, Content-Type: application/json }然后请求地址类似GET https://你的服务名.search.azure.cn/indexes?api-version2023-07-01-Preview这里最关键的就是get_token的 scope。中国区必须是https://search.azure.cn/.default如果你沿用全球版的https://search.azure.com/.default拿到的 Token 在调用中国区接口时会被判定为无效报错信息却千奇百怪。我在一次调试中就见过它报“权限不足”实际上 Token 根本就不是发给当前资源的。4. 中国区接入最常见的 4 类报错与排查方法4.1 报 401 UnauthorizedToken 明明拿到了这个现象最坑人你确认了代码已经成功获取 Token可请求 AI Search 时仍然收到 401。我的排查经验是从三个层面顺藤摸瓜。第一层确认 Token 的 audience。把拿到的 Token 复制出来做一次本地解码看一下aud字段。如果它写的是https://search.azure.com那你请求的却是https://你的服务名.search.azure.cn认证方直接判定我根本不认识这个 Token当然 401。第二层确认角色分配是否生效。RBAC 角色分配是有传播延迟的通常 30 秒到 1 分钟完全没问题但在大企业租户里有时会拖到 5 分钟。如果你刚分配完角色就立刻测试建议等一会儿再跑一次。第三层确认你分配的角色是数据平面角色而不是控制平面角色。如果只给了一个资源本身的 Reader 角色它并不能用来访问索引数据。4.2 报 403 Forbidden可能不是权限问题403 的情况比 401 更微妙。403 通常意味着你已经通过了身份认证但是授权检查没过。但我在中国区遇到过一种迷惑情况用管理密钥能连换成 RBAC 之后立刻 403日志里还看不到明确的错误原因。真正常见的原因是“索引数据接口没有默认开放”。AI Search 的索引数据接口例如/indexes/xxx/docs/search如果启用了共享密钥而你改用 RBAC 模式需要确保请求中不再带任何api-key头。因为一旦带了api-key服务会选择密钥认证路径如果密钥无效或者和身份不一致就会 403。换句话说认证方式要二选一别混着传头。另外如果你用的是 Python SDK代码里传入了 credential 就不应在 headers 里再手动塞api-keySDK 会自动处理。如果你在 REST 层自己封装了请求务必检查 headers 里有没有残留的api-key。4.3 报 AADSTS70011 或 Invalid audience属于配置型错误AADSTS70011 这个错误码一出基本就是 scope 参数格式不对。常见原因是把 scope 写错了比如漏了/或者少了.default后缀。注意中国区的正确写法是https://search.azure.cn/.default注意/.default是必须的它表示使用应用默认权限范围。有些人在写资源 ID 时只写了https://search.azure.cn会导致 Entra ID 返回 scope 无效。如果你用的是 SDK 自动推导 audience 的方式一般不会出现这个错手动构造请求时最容易踩。4.4 角色刚分配完为什么还是访问不了这个问题我实在太熟了几乎每次演示都会有人问。除了前面说到的传播延迟还有三种可能性你给的角色分配对象不对。在 IAM 里搜索的是应用名称但仍有概率匹配到同名的用户或者组要仔细看“名称”和“对象类型”。客户端缓存了 Token。ClientSecretCredential默认会缓存 Token直到过期。如果你分配角色后立刻用同一个进程测试缓存里的 Token 可能是在角色分配前签发的权限信息尚未包含。最直接的验证办法是重启进程让旧的缓存失效。你测试用的服务主体和应用注册不是同一个。在大型企业里应用注册很多搜索名字时容易选错。建议在 IAM 分配页面勾选后查看一下服务主体的对象 ID再去 Entra ID 对应应用注册的“概述”里核对一下是否一致。4.5 常见问题速查表我做了一个速查表遇到问题可以先对号入座现象可能原因解决方向401 Unauthorizedaudience 不对 / 未授权 / 认证头缺失解码 Token 看 aud检查角色分配去掉 api-key 头403 Forbidden混用了 api-key 与 RBAC请求中只保留 Bearer Token移除 api-keyAADSTS70011scope 格式错误使用https://search.azure.cn/.defaultInvalid audience用了全球版 SDK 默认值显式设置 SearchAudience.AzureChina可以登录但找不到索引角色分配未传播或选错角色等待传播核对 Search Index Data Reader/Contributor机密刚更新就报认证失败缓存了旧 Token / 机密未及时同步重启进程确认机密“值”已同步到配置中SDK 提示 login.microsoftonline.com 无法访问登录终结点仍指向全球版设置 authority 为https://login.partner.microsoftonline.cn5. 最后聊几点我在实操中踩出来的经验整个过程走下来我最强烈的感受是在中国区连接 AI Search 的难点不在 RBAC 本身而在“环境差异”上。RBAC 的思路全球版大厂都差不多网上也有大把教程可那些教程默认是对着全球版环境写的。你把教程里的 endpoint、authority、audience 原封不动拿过来用十个里有八个会失败。以后再看任何 AI Search 认证的代码示例我都建议先确认三个问题它用的登录终结点是不是中国区的它访问的资源域名是不是.search.azure.cn它请求的 scope 是不是https://search.azure.cn/.default这三个问题里有一个没对上代码写得再优雅都没用。还有一个特别值得分享的小技巧调试认证问题时别急着看代码逻辑先把 Token 和报错日志拆开看。Token 里包含了签发时间、过期时间、audience、issuer 等关键信息很多时候一眼就能定位问题。工作里为了快速解码 Token我习惯在本地放一个很小的脚本来解析 JWT 的 payload哪里不对改哪里比反复重启服务高效得多。如果你是想把现有服务从 API Key 迁移到 RBAC我的建议是分两步走先在测试环境把整套链路跑通确认数据读写正常再在生产环境给应用注册分配角色、逐步切换。切换期间不要同时删掉老的 API Key新旧并存一段时间看着流量日志确认没有异常再回收。这样既能把风险降下来也方便安全团队做审计。最后如果你们的服务是跑在 Azure 内部的我真心推荐一步到位用托管身份。少了 secret 的配置、轮换和泄漏风险代码里连密钥存储的依赖都不用加。连接代码只差一行 credential 的区别却能省掉未来大半的运维烦恼。还是那句话身份认证这种事前期多做一点设计后面就能多睡几个安稳觉。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →