簡體   English   中英

使用 ASP.Net Core 中的 Authorize 屬性返回 HTTP 403

[英]Return HTTP 403 using Authorize attribute in ASP.Net Core

使用 ASP.Net WebAPI 時,我曾經有一個自定義的Authorize屬性,我會根據情況使用它來返回 HTTP 403401 例如,如果用戶未通過身份驗證,則返回401 如果用戶已通過身份驗證但沒有適當的權限,則返回403 有關更多討論請參見此處

現在看來,在新的 ASP.Net Core 中,他們不希望您再覆蓋Authorize屬性,而是支持基於策略的方法。 但是,Core MVC 似乎遭受與其前輩相同的“所有身份驗證錯誤僅返回401 ”方法的困擾。

如何覆蓋框架以獲得我想要的行為?

此處打開一個問題后,看起來這實際上應該起作用......有點。

在您的Startup.Configure ,如果您只調用app.UseMvc()並且不注冊任何其他中間件,您將收到401任何與身份驗證相關的錯誤(未經過身份驗證、經過身份驗證但沒有權限)。

但是,如果您注冊支持它的身份驗證中間件之一,您將正確獲得401 (未經身份驗證)和403 (無權限)。 對我來說,我使用了JwtBearerMiddleware ,它允許通過JSON Web Token進行身份驗證。 關鍵部分是在創建中間件時設置AutomaticChallenge選項:

Startup.Configure

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

AutomaticAuthenticateAutomaticAuthenticate設置ClaimsPrincipal以便您可以在控制器中訪問User AutomaticChallenge允許 auth 中間件在發生 auth 錯誤時修改響應(在這種情況下,適當地設置401403 )。

如果您有自己的身份驗證方案要實現,您將繼承AuthenticationMiddlewareAuthenticationHandler類似於JWT 實現的工作方式

我最終用中間件來做:

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中注冊。

在 ASP.NET Core 中使用 IAuthorizationPolicyProvider遵循了自定義授權策略提供程序的指南,並且還想創建一個自定義響應。

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

我遵循的指南是自定義授權中間件的行為

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);
    }
}

啟動.cs:

services.AddSingleton<IAuthorizationMiddlewareResultHandler,
    GuidKeyAuthorizationMiddlewareResultHandler>();

您還可以編輯AuthorizationHandler並通過IHttpContextAccessor訪問IHttpContextAccessor 然而,這感覺更像是一個黑客。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM