ASP.NET 基于核心策略的授权中的 UnauthorizedAccessException 行为

UnauthorizedAccessException behavior in ASP.NET Core policy based authorization

我有一个简单的 ASP.NET Core API (.net 5) 应用程序,其安全配置如下:

    services.AddControllers(options =>
        options.Filters.Add(new MyExceptionFilter()));
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)     
       .AddMicrosoftIdentityWebApi(Configuration.GetSection("AAD"));

    services.AddAuthorization(options =>
    {
        options.AddPolicy("MyPolicy", policy =>
           policy.RequireAssertion(context =>
               context.User.HasClaim(c => c.Type == "myclaim" && c.Value == "myvalue")));
    });

然后将策略应用于控制器:

    [ApiController]
    [Route("[controller]")]
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = "MyPolicy")]
    public class DefaultController : ControllerBase {}

当令牌没有必需的声明时,框架会抛出 System.UnauthorizedAccessException:

System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token. 
   at Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderExtensions.<>c__DisplayClass3_1.<<AddMicrosoftIdentityWebApiImplementation>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

没关系。不好的是它随后转换为 HTTP 500,尽管我期望 HTTP 403。问题是标准异常过滤器(继承自 Microsoft.AspNetCore.Mvc.Filters.ExceptionFilterAttribute)没有捕获它,可能是因为它在管道中发生得太早了。

为什么这样设计?如何将身份验证阶段的异常映射到我需要的任何内容?

好的...我知道了。感谢您更新了代码,没有它就无法追溯到这么深。

代码使用了属于Microsoft.Identity.Web包的AddMicrosoftIdentityWebApi。在屏幕后面,使用 JwtBearerOptionsJwtBearerHandler.

AddMicrosoftIdentityWebApi 的配置中,JwtBearerEvents 是要求范围和角色声明的硬代码,如您所见 here

http://schemas.microsoft.com/identity/claims/scopehttp://schemas.microsoft.com/ws/2008/06/identity/claims/role 特定于 .net 世界,scproles 特定于 Oauth 标准世界。 Jwt 令牌输入的内容必须至少有四个范围中的一个,否则会引发异常(如 JwtBearerEvents 调用验证 here, that directly point to here,它被硬编码为第一个 link 以上。)

关键是,异常会在 AuthenticationMiddleware 上抛出,这在我们的管道上非常高,在此处捕获 UnauthorizedAccessException 到 return 403 可能会吞下任何使用的自定义逻辑UnauthorizedAccessException 的我们希望从应用程序逻辑中抛出(在我的例子中,我在自己的项目中使用了这种异常)。

所以最合适的方法是确保 jwt 令牌输入具有我上面提到的四个范围之一(Microsoft.Identity.Web 包本身需要)。或使用放置在 AuthenticationMiddleware 上方的中间件,仅在消息包含 IDW10201 时捕获 UnauthorizedAccessException 异常,然后将响应从 500 写回 403。(我仍然更喜欢控制器下方的异常过滤器级别,因为我可以区分哪个异常来自我的逻辑,或者来自其他地方)。