从SPFx WebPart调用AD保护的Azure Function时遇401未授权问题
问题背景
尝试从SharePoint WebPart调用由AD保护的Azure Function,始终收到401未授权错误。已按教程配置但仍失败,且Azure门户的身份验证配置项已更新,官方文档存在滞后。
已完成的配置步骤
- 在本地创建默认Azure Function,设为匿名访问,返回字符串“Hello world”。
- 部署本地函数至Azure门户的Function App实例。
- 通过浏览器/PowerShell(
Invoke-WebRequest)测试函数,运行正常,返回“Hello World”。 - 为Azure Function配置身份验证与授权:
- 身份提供商:Microsoft
- 创建名为
AuthenticationFunctionAppAZF的应用注册 - 客户端密钥名称:
MICROSOFT_PROVIDER_AUTHENTICATION_SECRET - 颁发者URL:
https://login.microsoftonline.com/common/v2.0 - 允许的令牌受众:
8bdb826f-bbbc-4f6b-923a-63777a5c3550(应用注册客户端ID) - 客户端应用要求:仅允许来自自身应用的请求
- 身份要求:允许来自任何身份的请求
- 租户要求:仅允许来自指定租户的请求(使用自身租户ID)
- 在Azure Function对应的应用注册中配置:
- Web应用回调URL:
https://oauth.pstmn.io/v1/browser-callback(Postman测试用)、https://authenticationfunctionappazf.azurewebsites.net/.auth/login/aad/callback - 允许令牌:允许ID令牌
- 支持的账户类型:任何组织目录中的账户(多租户)
- Web应用回调URL:
- 配置API权限与公开API:
- 修改应用ID URI为
https://tenantname.onmicrosoft.com/ClientID_of_AzureAppRegistration_of_My_Function - 添加作用域:
user_impersonation、access_as_user
- 修改应用ID URI为
- 浏览器测试:登录Azure门户后调用函数URL可正常返回“Hello world!”。
SPFx WebPart调用代码
import * as React from 'react'; import { useState } from 'react'; import { AadHttpClient, IHttpClientOptions, HttpClientResponse } from '@microsoft/sp-http'; import { WebPartContext } from '@microsoft/sp-webpart-base'; export interface IHelloUserProps { context: WebPartContext; } export interface IHelloWorld { hello: string; } const HelloUser: React.FC<IHelloUserProps> = ({ context }) => { const [hello, setHello] = useState<IHelloWorld | null>(null); const fetchUserData = () => { console.log('Fetching user data...'); if (!context) { console.error('Context is undefined'); return; } console.log('Context:', context); if (!context.aadHttpClientFactory) { console.error('aadHttpClientFactory is undefined'); return; } console.log('aadHttpClientFactory:', context.aadHttpClientFactory); const options: IHttpClientOptions = { headers: { 'Accept': 'application/json;odata.metadata=none' } }; context.aadHttpClientFactory .getClient('https://artetechnology.onmicrosoft.com/8bdb826f-bbbc-4f6b-923a-63777a5c3550') .then((client: AadHttpClient): void => { console.log('AadHttpClient obtained:', client); client .get('https://authenticationfunctionappazf.azurewebsites.net/api/AuthenticationFunctionAppAZF?', AadHttpClient.configurations.v1, options) .then((response: HttpClientResponse): Promise<any> => { console.log('Response received:', response); return response.json(); }) .then((user: any): void => { console.log('User data:', user); setHello(user); }) .catch((error: any): void => { console.error('Error parsing response:', error); }); }) .catch((error: any): void => { console.error('Error getting AadHttpClient:', error); }); }; return ( <div> <button onClick={fetchUserData}>Fetch User Data</button> {hello ? <div>Hello, {hello.hello}</div> : <div>Loading...</div>} </div> ); }; export default HelloUser;
问题现象
- SPFx本地工作台调用时返回401错误
- SharePoint站点中WebPart调用时同样返回401错误
- CORS已配置但未解决问题
技术排查与解决建议
1. 修正令牌受众配置
Azure Function身份验证设置中的允许的令牌受众应配置为应用注册的应用ID URI(即https://tenantname.onmicrosoft.com/ClientID_of_AzureAppRegistration_of_My_Function),而非应用注册的客户端ID。当前配置的受众不匹配SPFx请求的令牌受众,导致验证失败。
2. 对齐SPFx资源ID与应用ID URI
SPFx代码中getClient方法传入的资源ID必须与Azure Function应用注册的应用ID URI完全一致。检查当前代码中的https://artetechnology.onmicrosoft.com/8bdb826f-bbbc-4f6b-923a-63777a5c3550是否与实际配置的应用ID URI完全匹配(包括租户名称、客户端ID等细节)。
3. 调整客户端应用要求限制
当前设置的“仅允许来自自身应用的请求”会阻止SPFx的请求(SPFx使用SharePoint内置应用身份)。需将此选项修改为允许来自任何已注册应用的请求,或手动添加SPFx的应用ID(00000003-0000-0ff1-ce00-000000000000)到允许列表中。
4. 启用访问令牌并配置SPFx权限
- 在Azure Function身份验证设置中,除了允许ID令牌,还需允许访问令牌(access token),因为
AadHttpClient默认请求的是访问令牌。 - 在SPFx的
package-solution.json中添加对Azure Function的权限请求:
部署解决方案后,需在SharePoint管理员中心批准该权限。"webApiPermissionRequests": [ { "resource": "AuthenticationFunctionAppAZF", "scope": "user_impersonation" } ]
5. 完善CORS配置
- 添加本地开发地址:
https://localhost:4321 - 添加SharePoint站点根地址(如
https://yourtenant.sharepoint.com) - 确保“允许凭据”选项已勾选,因为SPFx请求会携带身份凭据。
6. 统一颁发者URL与租户设置
当前颁发者URL使用common端点,但租户要求限制为指定租户。将颁发者URL修改为指定租户的v2端点:https://login.microsoftonline.com/[你的租户ID]/v2.0,确保颁发者与租户限制一致。
7. 调试令牌内容
使用浏览器开发者工具捕获请求的Authorization令牌,通过JWT解析工具查看:
aud字段是否等于Azure Function的应用ID URIiss字段是否匹配指定租户的颁发者URLscp字段是否包含user_impersonation作用域
内容的提问来源于stack exchange,提问作者Jakub Husařík

