有没有办法在 .Net 5 中的 AuthorizationHandler 中重定向?

Posted

技术标签:

【中文标题】有没有办法在 .Net 5 中的 AuthorizationHandler 中重定向?【英文标题】:Is there a way to Redirect in AuthorizationHandler in .Net 5? 【发布时间】:2021-07-18 07:50:09 【问题描述】:

如果某些检查失败,我想重定向到我的 AuthorizationFilter 中的操作。我想重定向而不是将用户发送到“AccessDenied”视图的原因是为了防止用户知道端点在某些条件下存在。

一个更好的例子是当你创建一个登录系统时,你“不应该”明确地告诉用户用户名/电子邮件或密码是否错误。如果有人告诉他们用户名/电子邮件是正确的,他们就知道它存储在数据库中,现在他们只需要找出密码。

在 .Net Core 3.0 之前在 AuthorizationHandler 内部重定向时,我可以使用:

if (context.Resource is AuthorizationFilterContext redirectContext) 

    redirectContext.Result = new RedirectResult("/Account/NoPageHere");
    context.Succeed(requirement); 

但从 .Net Core 3.0+ 开始,这已更改并且不再受支持。所以我的问题是,可以从 AuthorizationHandler 重定向还是完全删除?

我可以使用的一个解决方案是通过 AuthorizationHandler 中的 HttpContextAccessor 将一些数据存储在 HttpContext.Items 中,然后根据该数据进行重定向。如果我的控制器继承自基础控制器,该基础控制器具有处理 HttpContext.Items 中的数据并决定我们是否应该重定向以及重定向到哪里的方法,这将工作得很好。唯一的问题是我需要在每个使用 AuthorizationFilter => pain 的 Action 中插入这个基类方法。我想将逻辑保存在一个地方,而不必复制粘贴代码。

如果有人有更好的建议,我很乐意听取他们的意见!

【问题讨论】:

您好,您找到正确重定向的方法了吗? (.net 5) 【参考方案1】:

在 AuthorizationHandler 内部没有找到任何好的重定向解决方案,所以我做了我能想到的第二好的事情,并在 AuthorizationHandler 之后创建了一个 TypeFilterAttribute 和一个用于存储重定向信息的服务。并在 ActionMethod 触发之前重定向。

我们的想法是在 AuthorizationHandler 中进行所有验证检查,但问题是我们必须始终验证请求。这并不理想,但如果不将请求设置为 Succeed(),我们无法根据我的知识进行重定向。当我们在授权处理程序中遇到一个或多个失败条件时,我们将重定向信息存储在 RedirectService 中,我们将在 TypeFilterAttribute 中再次使用它来重定向到正确的方法。您可以通过返回来提前停止 AuthorizationHandler,或者像我的示例一样继续添加具有优先级 Id 的潜在多重重定向。

TypeFilterAttribute 属于 IActionFilter 类型,并且具有将在 ActionMethod 之前和之后运行的方法。为了我的需要,我必须在 ActionMethod 之前检查,但如果需要,可以在任何其他点重定向。

基于我自己的代码添加了下面的代码示例,出于安全原因进行了更改,但希望它能够理解。

AuthorizationHandler

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MembershipRequirement requirement)
  
    // We preemptively set authorization as successful as we need to succeed to redirect.
    context.Succeed(requirement);
    
    var id = _service.GetId();
    if (id == 0)
    
        // Prio Id is used in case multiple redirects are set to find the most important ( 1 => most important )
        _redirectService.AddRedirect(1, MVC.Home.Error("404"));
    

    var name = _service.getName();
    if (name.toLower() != "cool")
    
        // Prio Id is used in case multiple redirects are set to find the most important ( 1 => most important )
        _redirectService.AddRedirect(2, MVC.Account.ChangeName());
    

重定向服务

public class RedirectService : IRedirectService

    public bool IsRedirecting  get; set; 
    public SortedDictionary<int, IActionResult> RedirectResults  get; set;  = new SortedDictionary<int, IActionResult>();
    
    // Ignores value if order ( key ) is already set
    public void AddRedirect(int order, IActionResult redirectResult)
    
        if (redirectResult == null || RedirectResults.ContainsKey(order)) return;
        
        IsRedirecting = true;
        RedirectResults.Add(order, redirectResult);
    


public interface IRedirectService

    public bool IsRedirecting  get; set; 
    public SortedDictionary<int, IActionResult> RedirectResults  get; set; 

    public void AddRedirect(int order, IActionResult redirectResult);

重定向类型过滤器属性:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RedirectFilterAttribute : TypeFilterAttribute

    public RedirectFilterAttribute() : base(typeof(RedirectFilterType)) 

    private class RedirectFilterType : IActionFilter
    
        private readonly IRedirectService _redirectService;
    
        public RedirectFilterType(IRedirectService redirectService)
        
            _redirectService = redirectService;
        

        public void OnActionExecuting(ActionExecutingContext context)
        
            // Check if we should redirect
            if (!_redirectService.IsRedirecting) return;
        
            var callInfo = _redirectService.RedirectResults.First().Value.GetR4ActionResult();
            context.Result = new RedirectToRouteResult(null, callInfo.RouteValueDictionary, null);
        

        public void OnActionExecuted(ActionExecutedContext context) 
    

Startup.cs 依赖注入

// Setup RedirectService
services.AddScoped<IRedirectService, RedirectService>();

// Setup AuthroiztionHandler(s)
services.AddScoped<IAuthorizationHandler, MembershipAuthorizationHandler>();

config.AddPolicy(nameof(MembershipRequirement.MembershipPolicy),
    policy => policy.Requirements.Add(new MembershipRequirement(isAdmin: false)));

config.AddPolicy(nameof(MembershipRequirement.AdminMembershipPolicy),
    policy => policy.Requirements.Add(new MembershipRequirement(isAdmin: true)));

PS:我正在使用 R4MVC(这太棒了),因此您需要更改在 TypeFilterAttribute 中重定向的方式。

【讨论】:

以上是关于有没有办法在 .Net 5 中的 AuthorizationHandler 中重定向?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 WinForms .NET 3.5 应用程序中为未处理的异常定义操作?

有没有办法用 .Net 3.5 在 WPF 中模拟 UseLayoutRounding

有没有办法让 Asp.net 零公共站点(Asp.net MVC)中的实时登录用户?

有没有办法在 .NET 运行时预分配堆,比如 Java 中的 -Xmx/-Xms?

有没有办法拦截和修改asp.net中的html输出流,以结合javascript?

有没有办法为 ASP.NET Core 中的内容设置通用根路径?