Asp.Net 样板 .Net Core 2.0 AbpAuthorizationFilter - ChallengeResult / 未经授权

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

我是 运行 Asp.Net Core 2.0 Asp.Net 样板版本 3.4.0 网络应用程序。

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

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

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

我注意到在ABP的.Net框架版本中,AbpMvcAuthorizeFilter(即HandleUnauthorizedRequest)中有可重写的方法;但是,这不适用于 .Net Core 版本。请参阅 GitHub 问题 1256 -> Make AbpMvcAuthorizeFilter and AbpApiAuthorizeFilter overridable

问题:

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

如果我采用变通方法,控制这种行为感觉有点像破解。

选项 1:

在我列出的三个选项中,选项 one 似乎是处理此逻辑的最干净、最合适的位置。它也不漂亮,因为我将复制整个 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 通过 HandleChallengeAsync 方法在 CookieAuthenticationHandler 中引发。话虽如此,可以安全地假设此事件只会由 ChallengeResult 结果引发。 CookieAuthenticationHandler.cs

选项 3:

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

主要目标

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

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

  1. 引入最少的代码,无需复制整个授权过滤器,只需更改两行代码。
  2. 通过自己的一组角色和权限(参见下面的代码解决方案)更好地控制挑战体验(MVC 应用程序也是一个 OIDC 客户端)
  3. 在此响应时,OnRedirectToLogin 事件仅由 CookieAuthenticationHandler 中的 HandleChallengeAsync 引发,因此感觉这是覆盖 [=15= 的正确位置]行为。

解法:

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 returns 禁止结果。

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