【问题标题】:Return HTTP 403 using Authorize attribute in ASP.Net Core在 ASP.Net Core 中使用 Authorize 属性返回 HTTP 403
【发布时间】:2016-02-26 16:24:58
【问题描述】:

在使用 ASP.Net WebAPI 时,我曾经有一个 custom Authorize attribute,我会根据具体情况使用返回 HTTP 403401。例如如果用户未通过身份验证,则返回401;如果用户已通过身份验证但没有适当的权限,则返回 403See here for more discussion 那个。

现在看来,在新的 ASP.Net Core 中,他们don't want you overriding the Authorize attribute 不再支持基于策略的方法。然而,Core MVC 似乎遭受了与其前辈相同的“所有身份验证错误返回 401”的方法。

如何覆盖框架以获得我想要的行为?

【问题讨论】:

  • 根据@blowdart 的说法,如果当用户通过身份验证但未经授权时,MVC6 Authorize 属性仍然返回 401 而不是 403,则要么应该提交错误,要么您做错了什么。
  • 我只是假设它是设计使然,它会返回 401,无论场景如何。如果这种行为不是预期的,我将在 aspnet/security 中提交一个错误。
  • 在此处查看最后一条评论:stackoverflow.com/a/31465227/304832

标签: c# asp.net-core asp.net-core-mvc


【解决方案1】:

我最终使用了中间件:

public class AuthorizeCorrectlyMiddleware
{
    readonly RequestDelegate next;

    public AuthorizeCorrectlyMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        await next(context);

        if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
        {
            if (context.User.Identity.IsAuthenticated)
            {
                //the user is authenticated, yet we are returning a 401
                //let's return a 403 instead
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            }
        }
    }
}

在调用app.UseMvc()之前应该在Startup.Configure注册。

【讨论】:

  • 这是我要建议的,但不确定如何将其仅应用于某些控制器/操作,就像使用属性一样。
  • 这很危险,因为在使用 next(context) 调用下一个中间件/委托后写入 HttpResponse(在这种情况下通过设置状态码)将引发异常,以防管道中的其他中间件开始写入响应已经。
  • 忘了提及....在修改状态码之前,您至少应该检查 context.Response.HasStarted == false。
【解决方案2】:

打开issue here 后,看起来这实际上应该可以工作......有点。

在您的Startup.Configure 中,如果您只是调用app.UseMvc() 并且不注册任何其他中间件,您将收到401 用于任何与身份验证相关的错误(未通过身份验证,已通过身份验证但没有权限)。

但是,如果您注册了支持它的身份验证中间件之一,您将正确地获得 401 用于未经身份验证和 403 用于无权限。对我来说,我使用了JwtBearerMiddleware,它允许通过JSON Web Token 进行身份验证。关键部分是在创建中间件时设置AutomaticChallenge选项:

Startup.Configure:

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true
});
app.UseMvc();

AutomaticAuthenticate 将自动设置ClaimsPrincipal,以便您可以在控制器中访问UserAutomaticChallenge 允许 auth 中间件在发生 auth 错误时修改响应(在这种情况下适当设置 401403)。

如果您有自己的身份验证方案要实施,您可以从AuthenticationMiddlewareAuthenticationHandler 继承,类似于JWT implementation works

【讨论】:

  • 我没有使用 JwtBearerAuthentication,而是使用 cookie 身份验证。设置 AutomaticChallenge = true 会导致授权失败时自动重定向。我不想要重定向,但是,403。从我在文档中可以看到,这是不可能的。
  • Cookie auth 在 webforms 时代是默认的“FormsAuthentication”。我认为这种行为是以此为模型的。例如如果某人未通过身份验证,您希望将他们重定向到登录页面,之后将使用他们的身份验证令牌设置 cookie。如果您想要 cookie 身份验证和 JWT 的行为,您可能需要使用 AuthenticationMiddlewareAuthenticationHandler 实现自定义身份验证方案
  • 很好的答案。为我解决了一个大问题。谢谢!
  • 我们如何在 Identity Core 2 中使用它?那里没有这样的选择
【解决方案3】:

我遵循在 ASP.NET Core 中使用 IAuthorizationPolicyProvider 的自定义授权策略提供程序指南,并且还想创建一个自定义响应。

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-5.0

我遵循的指南是自定义 AuthorizationMiddleware 的行为

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/customizingauthorizationmiddlewareresponse?view=aspnetcore-5.0

我的代码最终看起来像这样:

public class GuidKeyAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
    private readonly AuthorizationMiddlewareResultHandler
         DefaultHandler = new AuthorizationMiddlewareResultHandler();

    public async Task HandleAsync(
        RequestDelegate requestDelegate,
        HttpContext httpContext,
        AuthorizationPolicy authorizationPolicy,
        PolicyAuthorizationResult policyAuthorizationResult)
    {

        if (policyAuthorizationResult.Challenged && !policyAuthorizationResult.Succeeded && authorizationPolicy.Requirements.Any(requirement => requirement is GuidKeyRequirement))
        {
            httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            return;
        }

        // Fallback to the default implementation.
        await DefaultHandler.HandleAsync(requestDelegate, httpContext, authorizationPolicy,
                               policyAuthorizationResult);
    }
}

Startup.cs:

services.AddSingleton<IAuthorizationMiddlewareResultHandler,
    GuidKeyAuthorizationMiddlewareResultHandler>();

您还可以编辑您的 AuthorizationHandler 并访问 httpContext 通过IHttpContextAccessor。然而,这感觉更像是一种 hack。

internal class GuidKeyAuthorizationHandler : AuthorizationHandler<GuidKeyRequirement>
{
    private readonly ILogger<GuidKeyAuthorizationHandler> _logger;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public GuidKeyAuthorizationHandler(ILogger<GuidKeyAuthorizationHandler> logger, IHttpContextAccessor httpContextAccessor)
    {
        _logger = logger;
        _httpContextAccessor = httpContextAccessor;
    }

    // Check whether a given GuidKeyRequirement is satisfied or not for a particular context
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, GuidKeyRequirement requirement)
    {
        var httpContext = _httpContextAccessor.HttpContext; // Access context here

        var key = System.Web.HttpUtility.ParseQueryString(httpContext.Request.QueryString.Value).Get("key");

        if (!string.IsNullOrWhiteSpace(key))
        {
            // If the user guid key matches mark the authorization requirement succeeded
            if (Guid.TryParse(key, out var guidKey) && guidKey == requirement.Key)
            {
                _logger.LogInformation("Guid key is correct");

                if (requirement.RequireRefererHeader)
                {
                    _logger.LogInformation("Require correct referer header");
                    httpContext.Request.Headers.TryGetValue("Referer", out var refererHeader);
                    if (requirement.RefererHeader == refererHeader)
                    {
                        _logger.LogInformation("Referer header is correct");
                        context.Succeed(requirement);
                        return Task.CompletedTask;
                    }
                    else
                    {
                        _logger.LogInformation($"Referer header {refererHeader} is not correct");
                    }
                }
                else
                {
                    _logger.LogInformation("Correct referer header is not needed");
                    context.Succeed(requirement);
                    return Task.CompletedTask;
                }
            }
            else
            {
                _logger.LogInformation($"Guid key {guidKey} is not correct");
            }
        }
        else
        {
            _logger.LogInformation("No guid key present");
        }
        var msg = "Invalid Guid";
        var bytes = Encoding.UTF8.GetBytes(msg);
        httpContext.Response.StatusCode = 403;
        httpContext.Response.ContentType = "application/json";
        httpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length);
        return Task.CompletedTask;
    }
}

在此处找到该解决方案:

https://stackoverflow.com/a/61861098/3850405

【讨论】:

    猜你喜欢
    • 2017-11-17
    • 2017-03-31
    • 2019-07-04
    • 1970-01-01
    • 2014-12-19
    • 2017-03-25
    • 1970-01-01
    • 2017-09-20
    • 1970-01-01
    相关资源
    最近更新 更多