首页 > 解决方案 > Asp.Net Boilerplate .Net Core 2.0 AbpAuthorizationFilter - ChallengeResult / 未授权

问题描述

我正在运行带有Asp.Net Boilerplate 3.4.0 版Web 应用程序的Asp.Net Core 2.0 。

当我有一个经过身份验证的用户没有访问资源所需的权限时,aChallengeResultAbpAuthorizationFilter通过Abp框架设置。此行为会导致用户返回到默认登录页面。如果用户通过身份验证,我想设置 aForbidResult并将它们重定向到默认的 AccessDenied 页面。

在查看了我的选项后,我发现我有以下选项:

  1. 添加我自己的授权过滤器并在服务配置MvcOptions之前注册。AddAbp()过滤器将有条件地将结果设置为挑战或禁止
  2. 覆盖OnRedirectToLogin事件(即,为经过身份验证的用户自定义)
  3. 覆盖HandleChallengeAsync处理程序(即检查请求是否经过身份验证,并为未经身份验证的请求发出 ForbidResult 或 ChallengeResult)

AbpMvcAuthorizeFilter我注意到在 ABP 的 .Net 框架版本中, (ie, )中有可覆盖的方法HandleUnauthorizedRequest;但是,这不适用于 .Net Core 版本。请参阅 GitHub 问题 1256 -> 使 AbpMvcAuthorizeFilter 和 AbpApiAuthorizeFilter 可覆盖

问题:

是否有其他人需要更改默认的Abp行为,即为ChallengeResult未经授权的请求返回 a?如果是,您使用了什么解决方案?我是否在Abp配置或 Asp.Net Core(除了上面列出的三个选项之外)中遗漏了一些可以让我更好地控制这种行为的东西?

如果我采用解决方法,控制这种行为感觉有点像黑客。

选项1:

在我列出的三个选项中,选项似乎是处理这个逻辑的最干净和最合适的地方。这也不是很漂亮,因为我会复制整个AbpAuthorizationFilter内容以仅更改几行代码。

当前代码:

context.Result = new ChallengeResult();

拟议变更:

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

完整代码如下:

AbpAuthorizationFilter.cs请参阅 Catch 块行 - 58 - 77

选项 2:

选项二感觉很笨拙,因为我将在事件中引入的逻辑OnRedirectToLogin必须假设经过身份验证的用户试图访问未经授权的资源。目前,我只看到通过方法Events.RedirectToLogin提出的问题。话虽如此,可以放心地假设此事件只会由结果引发。CookieAuthenticationHandler.csCookieAuthenticationHandlerHandleChallengeAsyncChallengeResult

选项 3:

选项三将是最后一个选项(即不惜一切代价避免)

主要目标

主要目标是为尝试访问未经授权资源的经过身份验证的用户提供更好的体验。与其将用户重定向到登录页面,不如将用户重定向到一个未授权/禁止的页面,该页面清楚地表明他们没有被授权。这可能包括提示用户提供更高特权凭据的选项。通过提示用户,它闻起来像一个ChallengeResult结果流,所以也许我只是回答了我自己的问题。对于当前的行为,我没有太多关于“为什么”发布的上下文信息ChallengeResult。我会知道用户已登录并且OnRedirectToLogin事件已引发。这可能是足够的信息来自定义的行为ChallengeResult对于经过身份验证的用户。这开始感觉这是正确的解决方案。关于使用这种方法的任何建议或反馈?

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

解决方案


在三个选项中,我选择了第二个选项(覆盖OnRedirectToLogin事件),原因如下:

  1. 在不复制整个授权过滤器的情况下引入了最少的代码,只更改了两行代码。
  2. 使用自己的角色和权限集更好地控制挑战体验(MVC 应用程序也是 OIDC 客户端)(请参阅下面的代码解决方案)
  3. 在此响应时,OnRedirectToLogin事件仅由CookieAuthenticationHandlerHandleChallengeAsync中的引发,因此这感觉像是覆盖行为的正确位置。ChallengeResult

解决方案:

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

边注:

应该注意的是,默认行为AbpAuthorizationFilter并不反映 Asp.Net Core MVC 2.0 的普通行为AuthorizeFilter。当经过身份验证的用户授权失败时,Asp.Net Core MVC 2.0 会AuthorizeFilter返回 Forbid 结果。

Asp.Net Core MVC 默认AuthorizeFilter将授权委托给 IPolicyEvaluator. 如果授权失败且用户通过身份验证,则设置 Forbid 结果,或者如果授权失败且用户未通过身份验证,则设置 Challenge 结果。

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

推荐阅读