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
。在屏幕后面,使用 JwtBearerOptions
和 JwtBearerHandler
.
在 AddMicrosoftIdentityWebApi
的配置中,JwtBearerEvents
是要求范围和角色声明的硬代码,如您所见 here。
http://schemas.microsoft.com/identity/claims/scope
和 http://schemas.microsoft.com/ws/2008/06/identity/claims/role
特定于 .net 世界,scp
和 roles
特定于 Oauth 标准世界。 Jwt 令牌输入的内容必须至少有四个范围中的一个,否则会引发异常(如 JwtBearerEvents
调用验证 here, that directly point to here,它被硬编码为第一个 link 以上。)
关键是,异常会在 AuthenticationMiddleware
上抛出,这在我们的管道上非常高,在此处捕获 UnauthorizedAccessException
到 return 403 可能会吞下任何使用的自定义逻辑UnauthorizedAccessException
的我们希望从应用程序逻辑中抛出(在我的例子中,我在自己的项目中使用了这种异常)。
所以最合适的方法是确保 jwt 令牌输入具有我上面提到的四个范围之一(Microsoft.Identity.Web
包本身需要)。或使用放置在 AuthenticationMiddleware
上方的中间件,仅在消息包含 IDW10201
时捕获 UnauthorizedAccessException
异常,然后将响应从 500 写回 403。(我仍然更喜欢控制器下方的异常过滤器级别,因为我可以区分哪个异常来自我的逻辑,或者来自其他地方)。
我有一个简单的 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
。在屏幕后面,使用 JwtBearerOptions
和 JwtBearerHandler
.
在 AddMicrosoftIdentityWebApi
的配置中,JwtBearerEvents
是要求范围和角色声明的硬代码,如您所见 here。
http://schemas.microsoft.com/identity/claims/scope
和 http://schemas.microsoft.com/ws/2008/06/identity/claims/role
特定于 .net 世界,scp
和 roles
特定于 Oauth 标准世界。 Jwt 令牌输入的内容必须至少有四个范围中的一个,否则会引发异常(如 JwtBearerEvents
调用验证 here, that directly point to here,它被硬编码为第一个 link 以上。)
关键是,异常会在 AuthenticationMiddleware
上抛出,这在我们的管道上非常高,在此处捕获 UnauthorizedAccessException
到 return 403 可能会吞下任何使用的自定义逻辑UnauthorizedAccessException
的我们希望从应用程序逻辑中抛出(在我的例子中,我在自己的项目中使用了这种异常)。
所以最合适的方法是确保 jwt 令牌输入具有我上面提到的四个范围之一(Microsoft.Identity.Web
包本身需要)。或使用放置在 AuthenticationMiddleware
上方的中间件,仅在消息包含 IDW10201
时捕获 UnauthorizedAccessException
异常,然后将响应从 500 写回 403。(我仍然更喜欢控制器下方的异常过滤器级别,因为我可以区分哪个异常来自我的逻辑,或者来自其他地方)。