添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

When using ASP.Net WebAPI, I used to have a custom Authorize attribute I would use to return either an HTTP 403 or 401 depending on the situation. e.g. if the user is not authenticated, return a 401 ; if the user is authenticated but doesn't have the appropriate permissions, return a 403 . See here for more discussion on that.

It seems now, in the new ASP.Net Core, they don't want you overriding the Authorize attribute anymore instead favoring a policy-based approach. However, it seems Core MVC suffers from the same "just return 401 for all auth errors" approach its predecessors have.

How do I override the framework to get the behavior I want?

According to @blowdart, if the MVC6 Authorize attribute is still returning 401's instead of 403's when a user is authenticated but unauthorized, there is either a bug that should be filed or you are doing something wrong. danludwig Feb 26, 2016 at 19:11 I just assumed it was by design that it was returning 401's no matter the scenario. If that behavior is not expected, I'll file a bug in aspnet/security. Chad Feb 26, 2016 at 21:12

After opening an issue here , it appears this actually should work...sort of.

In your Startup.Configure , if you just call app.UseMvc() and don't register any other middleware, you will get 401 for any auth-related errors (not authenticated, authenticated but no permission).

If, however, you register one of the authentication middlewares that support it, you will correctly get 401 for unauthenticated and 403 for no permissions. For me, I used the JwtBearerMiddleware which allows authentication via a JSON Web Token . The key part is to set the AutomaticChallenge option when creating the middleware:

in Startup.Configure :

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

AutomaticAuthenticate will set the ClaimsPrincipal automatically so you can access User in a controller. AutomaticChallenge allows the auth middleware to modify the response when auth errors happen (in this case setting 401 or 403 appropriately).

If you have your own authentication scheme to implement, you would inherit from AuthenticationMiddleware and AuthenticationHandler similar to how the JWT implementation works.

I'm not using JwtBearerAuthentication, but, using cookie auth. Setting AutomaticChallenge = true results in automatic redirects when failing authorization. I don't want redirects, but, 403's. From what I can see in the docs, this isn't possble. – steamrolla Jun 23, 2016 at 23:33 Cookie auth was the default back with "FormsAuthentication" in the webforms days. I think the behavior is modeled off of that. e.g. if someone isn't authenticated, you want to redirect them to a login page after which a cookie will be set with their auth token. If you want cookie auth AND the behavior of JWT, you will probably need to implement a custom authentication scheme using AuthenticationMiddleware & AuthenticationHandler – Chad Jun 25, 2016 at 12:36 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;

which should be registered in Startup.Configure before calling app.UseMvc().

This is what I was going to suggest, but not sure how you could apply it only to certain controllers / actions like you can with an attribute. – danludwig Feb 26, 2016 at 18:57 this is dangerous because writing to a HttpResponse (by setting statuscode in this case) after calling next middleware/delegate with next(context) will throw exception in case when some other middleware in the pipeline started writing response already. – dee zg Feb 5, 2017 at 20:13 forgot to mention....You should at least check context.Response.HasStarted == false before modifying the statuscode. – dee zg Feb 5, 2017 at 20:19

I followed the guide for Custom Authorization Policy Providers using IAuthorizationPolicyProvider in ASP.NET Core and also wanted to create a custom response.

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

The guide I followed for that was Customize the behavior of AuthorizationMiddleware

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

My code finally looked like this:

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

You can also edit your AuthorizationHandler and access httpContext via IHttpContextAccessor. However this feels more like a 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;
                        _logger.LogInformation($"Referer header {refererHeader} is not correct");
                    _logger.LogInformation("Correct referer header is not needed");
                    context.Succeed(requirement);
                    return Task.CompletedTask;
                _logger.LogInformation($"Guid key {guidKey} is not correct");
            _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;

Found that solution here:

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

Thank you. I tried to follow original SampleAuthorizationMiddlewareResultHandler but AuthorizationFailure is always null. I didn't find a way how to fill it in my AuthorizationHandler. So, finally fallen back to your solution: policy.Requirements.Any(requirement => requirement is MyRequirement) instead. – Niksr Mar 17 at 15:11

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.