Azure AD实现SSO后React调用.NET Core Web API报401未授权
按以下优先级逐一排查,覆盖绝大多数配置错误场景:
1. 修正中间件顺序(最高频错误)
你当前Startup.cs的中间件顺序完全错误,CORS中间件放在了UseEndpoints之后,不会对请求生效,跨域预检请求(OPTIONS)会直接被认证中间件拦截返回401。
将Configure方法里的中间件调整为以下正确顺序:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // 跨域配置必须放在Routing之后,认证、授权、端点映射之前 app.UseCors(x => x .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "TMS v1"); }); }
原配置中空参数的WithHeaders()、WithExposedHeaders()无实际作用,直接删除即可。
2. 确认调用API使用了正确的访问令牌
你前端loginRequest里申请的User.Read是微软Graph API的权限,绝对不能拿Graph的令牌调用自己的Web API:Azure AD按资源维度签发令牌,Graph令牌的受众(aud声明)是https://graph.microsoft.com,你的API会直接拒绝该令牌。
调用自有API前,必须调用MSAL实例的acquireTokenSilent方法,传入apiConfig.scopes单独申请对应自有API的访问令牌,再将该令牌以Authorization: Bearer <token>的格式放到请求头中。
拿到令牌后先解析令牌内容,核对以下核心声明:
aud(受众):必须和服务端配置的合法受众完全匹配,要么是服务端Azure应用的ClientId(guid格式),要么是你配置的应用ID URI(api://xxx格式)scp(委托权限):必须包含你配置的User.Read权限iss(签发者):格式必须为https://sts.windows.net/<你的租户ID>/,和服务端配置的TenantId一致- 开启角色校验的场景下,
roles声明必须包含Admin值
3. 修复服务端受众配置不匹配问题
你当前服务端AzureAd配置仅填写了ClientId(guid格式),但你暴露的API使用api://<app-id>/User.Read格式的应用ID URI。默认情况下AddMicrosoftIdentityWebApi仅将ClientId作为合法受众,如果令牌的aud是api://开头的URI,会直接因受众校验失败返回401。
在AzureAd配置节点中补充Audience项:
"AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "example.onmicrosoft.com", "ClientId": "392xxxx2-bxx4-4xxf-axxc-505bd9c6d8b4", "TenantId": "06xxx2xbe-9xxe-4xx8-bxxd-e1a6ebxxxxd", "scopes": "api://3xxxxe52-bxx4-4xxf-axx2c-505bxxxxb4/User.Read", "Audience": "api://3xxxxe52-bxx4-4xxf-axx2c-505bxxxxb4" }
注意:你贴出的配置中,服务端ClientId、scopes里的应用ID、前端apiConfig里的scope应用ID三个值打码后长度不一致,大概率是复制时粘贴错误,必须确保这几个值和Azure门户中服务端应用的实际配置完全一致,单个字符错误都会导致校验失败。同时核对Azure门户「公开API」页面的应用程序ID URI,必须和scope前缀完全匹配。
4. 缩小问题定位范围
临时注释Controller上的[RequiredScope]特性、Action上的[Authorize(Roles ="Admin")]特性,添加一个无授权要求的测试接口,根据返回结果定位问题:
- 去掉授权特性后仍返回401:问题出在CORS配置、令牌受众不匹配、令牌签发者不合法、前端使用了错误资源的令牌这几个环节
- 去掉特性后可正常访问,加回
[RequiredScope]就返回401:说明令牌中无对应scp声明,需检查:Azure门户中是否给前端客户端授予了服务端API的委托权限(不是应用权限,SPA无法使用应用权限)、是否完成了管理员同意、前端申请令牌时传入的scope是否正确 - 加回scope校验可正常访问,加回Roles校验返回401/403:说明令牌中无对应
roles声明,需在服务端Azure应用中配置App Role,并给对应用户/用户组分配Admin角色。
用户的访问权限(角色、权限范围)必须配置在服务端对应的Azure AD应用注册中:
- 客户端应用仅需配置「可访问的服务端API及对应委托权限」,也就是在客户端应用的「API权限」页面添加服务端暴露的scope即可,不需要在客户端侧配置任何用户角色/权限规则
- 服务端应用负责定义权限范围(Scope)、应用角色(App Role),用户/用户组的角色分配、权限校验逻辑全部在服务端侧完成,令牌中的
scp、roles声明均由Azure AD根据服务端应用的配置签发。
内容的提问来源于stack exchange,提问作者Alina Ishaque

