如何让失败的 AuthorizeAttribute 返回 HTTP401?
Posted
技术标签:
【中文标题】如何让失败的 AuthorizeAttribute 返回 HTTP401?【英文标题】:How to get failed AuthorizeAttribute to return HTTP401? 【发布时间】:2020-04-17 18:08:40 【问题描述】:我正在尝试向 ASP.NET Core 3.1 Web 服务添加身份验证,该服务会查找特定的自定义请求标头:
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
[HttpPut]
[Authorize(Policy = "MustSupplyAuthenticationToken")]
public async Task<ActionResult> putMyStuff()
// ...
我已将“MustSupplyAuthenticationToken”配置为使用我的 AuthenticationTokenRequirement 类,并连接 IAuthorizationHandler 以使用我的 AuthenticationTokenHandler(并连接 HttpContextFactory,因为我将需要它。):
public void ConfigureServices(IServiceCollection services)
services.AddControllers();
services.AddHttpContextAccessor();
services.AddAuthorization(options =>
options.AddPolicy("MustSupplyAuthenticationToken",
policy => policy.Requirements.Add(new AuthenticationTokenRequirement("MY_SECRET_TOKEN")));
);
services.AddTransient<IAuthorizationHandler, AuthenticationTokenHandler>();
我的 AuthenticationTokenRequirement 很简单:
public class AuthenticationTokenRequirement : IAuthorizationRequirement
public readonly string authenticationToken;
public AuthenticationTokenRequirement(string authenticationToken)
this.authenticationToken = authenticationToken;
而我的 AuthenticationTokenHandler 并没有那么复杂:
public class AuthenticationTokenHandler : AuthorizationHandler<AuthenticationTokenRequirement>
private readonly IHttpContextAccessor httpContextAccessor;
public AuthenticationTokenHandler(IHttpContextAccessor httpContextAccessor)
this.httpContextAccessor = httpContextAccessor;
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
AuthenticationTokenRequirement requirement)
var httpContext = this.httpContextAccessor.HttpContext;
var request = httpContext.Request;
var authenticationToken = request.getRequestHeader("authenticationToken");
if (authenticationToken == null)
context.Fail();
else if (authenticationToken != requirement.authenticationToken)
context.Fail();
else
context.Succeed(requirement);
return Task.CompletedTask;
令人惊讶的是,这一切都奏效了。 HandleRequirementAsync() 被调用,当我调用 context.Fail() 访问被拒绝,当我调用 context.Succeed() 访问被允许。
唯一的问题是,当我调用 context.Fail() 时,响应返回 HTTP 500 - 我需要它返回 HTTP 401。(当我开始工作时,我需要返回 403 的不同策略。)
我是不是做错了什么,因为其他错误而得到 500?
还是失败的身份验证策略应该返回 500?
我需要做什么才能让它返回 401?
FWIW:我在服务器日志中看到了这一点:
2019-12-27T15:49:04.2221595-06:00 80000045-0001-f900-b63f-84710c7967bb [ERR] An unhandled exception has occurred while executing the request. (48a46595)
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).
at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.<Invoke>g__AwaitMatcher|8_0(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task`1 matcherTask)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
这可能就是我看到 500 的原因。
但只有当我调用 context.Fail() 时。当我调用 context.Succeed() 时,我没有。
那么,当我未能满足要求时,为什么会收到“未指定身份验证方案”?
【问题讨论】:
看看这个:***.com/questions/52008000/…. 【参考方案1】:好的,因为我正在尝试做的策略根本不是正确的方法。
我需要做的是配置一个身份验证方案。
在提取了上面所有的授权策略后,我添加了这个。
在 AuthorizeAttribute 中,我指定了一个身份验证方案:
[HttpPut]
[Authorize(AuthenticationSchemes = "AuthenticationTokenScheme")]
public async Task<ActionResult> putTicketDeliveryModel()
// ...
在 ConfigureServices 中,我添加了方案:
services.AddAuthentication()
.AddScheme<AuthenticationTokenOptions, AuthenticationTokenHandler>("AuthenticationTokenScheme", _ => );
然后我实现了我的 AuthenticationTokenOptions 和 AuthenticationTokenHandler 类:
public class AuthenticationTokenOptions : AuthenticationSchemeOptions
public class AuthenticationTokenHandler : AuthenticationHandler<AuthenticationTokenOptions>
private readonly string expectedAuthenticationToken;
public AuthenticationTokenHandler(IOptionsMonitor<AuthenticationTokenOptions> optionsMonitor,
ILoggerFactory loggerFactory, UrlEncoder urlEncoder, ISystemClock systemClock,
IConfiguration config)
: base(optionsMonitor, loggerFactory, urlEncoder, systemClock)
this.expectedAuthenticationToken = config.GetSection("ExpectedAuthenticationToken").Get<string>();
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
var authenticationToken = this.Request.getRequestHeader("authenticationToken");
if (string.IsNullOrWhiteSpace(authenticationToken))
return Task.FromResult(AuthenticateResult.NoResult());
if (this.expectedAuthenticationToken != authenticationToken)
return Task.FromResult(AuthenticateResult.Fail("Unknown Client"));
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(Enumerable.Empty<Claim>(), Scheme.Name));
var authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(authenticationTicket));
如果我传递了错误的令牌,这将返回 401。
还没想好怎么返回403。
【讨论】:
以上是关于如何让失败的 AuthorizeAttribute 返回 HTTP401?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法让 AuthorizeAttribute 响应状态码 403 Forbidden 而不是重定向?
如何绕过 System.Web.Http.AuthorizeAttribute.IsAuthorized
ASP.NET Core 中如何通过 AuthorizeAttribute 做自定义验证?
两个(几乎)并发 DbContext 导致问题:如何在 Controller 和 AuthorizeAttribute 之间共享