简体   繁体   中英

Asp.Net Boilerplate .Net Core 2.0 AbpAuthorizationFilter - ChallengeResult / Unauthorized

I am running an Asp.Net Core 2.0 with Asp.Net Boilerplate version 3.4.0 web application.

When I have an authenticated user that does not have the required permissions to access a resource, a ChallengeResult is set by the AbpAuthorizationFilter via the Abp framework. This behavior results in the user being returned to the default login page. If the user is authenticated, I would like to set a ForbidResult and redirect them to the default AccessDenied page.

After reviewing my options, I see that I have the following options:

  1. Add my own authorization filter and register with the MvcOptions prior to the AddAbp() service configuration. The filter would conditionally set the result to Challenge or Forbid
  2. Override the OnRedirectToLogin event ( ie, customize for authenticated users )
  3. Override the HandleChallengeAsync handler ( ie, check to see if the request is authenticated and issue ForbidResult or ChallengeResult for unauthenticated requests)

I noticed in the .Net framework version of ABP, that there are overridable methods in the AbpMvcAuthorizeFilter (ie, HandleUnauthorizedRequest ) ; however, this does not apply to the .Net Core version. See GitHub Issue 1256 -> Make AbpMvcAuthorizeFilter and AbpApiAuthorizeFilter overridable

Question:

Has anyone else needed to change the default Abp behavior of returning a ChallengeResult for unauthorized requests? If yes, what solution did you use? Did I missing something in the Abp configuration or Asp.Net Core ( other than the three options listed above) that would provide me more control of this behavior?

If I go with a workaround, it feels kind of a like a hack to control this behavior.

Option 1:

Out of the three options that I listed, option one seems the cleanest and most appropriate place to handle this logic. It's not pretty either as I would be copying the entire AbpAuthorizationFilter to only change a few lines of code.

Current Code:

context.Result = new ChallengeResult();

Proposed Change:

if (context.HttpContext.User.Identity.IsAuthenticated)
{
    //User is already logged in.. No need to redirect to the 
    //login page
    context.Result = new ForbidResult();
}
else
{
    context.Result = new ChallengeResult();
}

Full code below:

AbpAuthorizationFilter.cs See Catch block lines - 58 - 77

Option 2:

Option two feels kludgy because the logic that I would introduce into the OnRedirectToLogin event would have to make an assumption that an authenticated user has attempted to access an unauthorized resource. Currently, I only see the Events.RedirectToLogin being raised in the CookieAuthenticationHandler via the HandleChallengeAsync method. With that being said it feels safe to assume that this event would only be raised by a ChallengeResult result. CookieAuthenticationHandler.cs

Option 3:

Option three would be the last option( ie, avoided at all costs)

Main Goal

The main goal is to provide a better experience for authenticated users attempting to access unauthorized resources. Rather than redirecting the user to the login page, the user should be redirected to an unauthorized / forbidden page that clearly indicates that they are not authorized. This might include an option to prompt the user to provide higher privileged credentials. By prompting the user, it smells like a ChallengeResult result flow so maybe I just answered my own question. With the current behavior, I don't have much context information on "why" the ChallengeResult was issued. I will know that the user is logged in and that the OnRedirectToLogin event was raised. This is probably enough info to customize the behavior of the ChallengeResult for authenticated users. This is starting to feel like this is the correct solution. Any suggestions or feedback on using this approach?

Out of the three options, I went with option number two ( override the OnRedirectToLogin event) for the following reasons:

  1. Minimal code introduced without duplicating an entire authorization filter to only change two lines of code.
  2. More control of the challenge experience ( the MVC app is an OIDC client too ) with its own set of roles and permissions ( see code solution below )
  3. At the time of this response, the OnRedirectToLogin event is only raised by the HandleChallengeAsync in the CookieAuthenticationHandler so this feels like the correct place to override the ChallengeResult behavior.

Solution:

options.Events.OnRedirectToLogin = context =>
{
    if (context.HttpContext?.User?.Identity?.IsAuthenticated == false)
    {
        //The user is not authenticated... Use the "oidc" challenge scheme 
        //and send them to identity server.
        var task = context.HttpContext.ChallengeAsync("oidc");
        task.WaitAndUnwrapException();

        return Task.CompletedTask;
    }

    var accessDeniedPath = BuildRedirectUri(context.HttpContext, options.AccessDeniedPath);

    context.Response.Redirect(accessDeniedPath);
    context.Response.StatusCode = 302;

    return Task.CompletedTask;
};

Side Note:

It should be noted that the default behavior of the AbpAuthorizationFilter does not mirror the stock behavior of Asp.Net Core MVC 2.0 AuthorizeFilter . When authorization fails for an authenticated user, the Asp.Net Core MVC 2.0 AuthorizeFilter returns a Forbid result.

Asp.Net Core MVC's default AuthorizeFilter delegates the authorization to the IPolicyEvaluator . If the authorization fails and the user is authenticated, a Forbid result is set or if the authorization fails and the user is not-authenticated, a Challenge result is set.

PolicyEvaluator.cs

var result = await _authorization.AuthorizeAsync(context.User, resource, policy);
if (result.Succeeded) 
{ 
    return PolicyAuthorizationResult.Success(); 
} 

// If authentication was successful, return forbidden, otherwise challenge 
return (authenticationResult.Succeeded)  
    ? PolicyAuthorizationResult.Forbid()  
    : PolicyAuthorizationResult.Challenge(); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM