ASP.Net 5 授权处理程序失败重定向

3
我正在尝试添加一个自定义的授权策略,它可以检查在json配置文件中提供的分隔符分隔的组列表。我正在使用ASP.Net 5 - MVC 6,并配合Windows身份验证使用。

一切都很好,除了当我调用Fail时。这时什么也不会发生,会显示空白屏幕。这是我的HandleRequirementAsync方法。我已经尝试过各种任务结果的值。我一直在像疯子一样搜索谷歌,但没有成功。希望有人能帮忙。

期望的结果:我想在失败时重定向到一个自定义页面,但如果不可能,至少能够重定向回登录页面。唯一似乎有任何影响的就是抛出异常。

启动中相关的注册代码:

var appSettings = Configuration.GetSection("AppSettings");
services.Configure<Models.AppSettings>(appSettings);

services.AddMvc();

services.AddAuthorization(options =>
{
    options.AddPolicy("RoleAuth", policy => policy.Requirements.Add(new RolesRequirement(appSettings["AllowedGroups"])));
});

services.AddSingleton<IAuthorizationHandler, RoleAuthorizationHandler>();

还有授权类:

public class RolesRequirement : IAuthorizationRequirement
{
    public RolesRequirement(string groups)
    {
        Groups = groups;
    }

    public string Groups { get; private set; }
}

public class RoleAuthorizationHandler : AuthorizationHandler<RolesRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesRequirement requirement)
    {
        if (!string.IsNullOrWhiteSpace(requirement.Groups))
        {
            Console.WriteLine(requirement.Groups);
            var groups = requirement.Groups.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            //we could check for group membership here.... maybe???
            foreach (var group in groups)
            {
                if (context.User.IsInRole(group))
                {
                    context.Succeed(requirement);
                    return Task.FromResult(0);
                }
            }
        }
        else
        {
            context.Succeed(requirement);
        }

        context.Fail();
        return Task.FromResult(0);
    }
}

目前还在寻找失败时重定向的方法,你有没有运气好过吗? - Alex Hope O'Connor
不行,Alex。没有运气。抱歉。 - SpaceGhost440
一个丑陋的解决方法是在AccountController.AccessDenied()方法中再次从HandleRequirementAsync()调用相同的代码并从那里重定向。在您的情况下,您需要所需的组,这使得此任务变得困难(反射等)。将内容添加到HttpContext.Items中不起作用,在到达AccessDenied()之前会被清除...最后(甚至更丑陋的)一招是在某个地方有一个静态的Dictionary<User,something>,在HandleRequirementAsync()中添加条目,并在AccessDenied()中再次删除它们。是的,如果有更好的方法,我也会对此进行投票... - Louis Somers
5个回答

3
我是一名有帮助的助手,可以为您进行文本翻译。以下是需要翻译的内容:

我找到的唯一方法是不使用context.Fail(),而是这样做:

替换为:

context.Fail();

使用:

var mvcContext = context.Resource as AuthorizationFilterContext;
mvcContext.Result = new RedirectToActionResult("Action", "Controller", null);
context.Succeed(requirement);

允许上下文成功,将执行现在是重定向的上下文。

1
据我所知,自 .NET Core 3 起,这已不再是一个选项。在核心 2.1 和 2.2 中肯定可以工作。 - Jon Selby

1

在尝试了Herotwists的答案并发现它在.NET Core中不再起作用(context.Resource as AuthorizationFilterContext总是返回NULL)之后,我想出了这个,在.NET 5中似乎可以正常工作。虽然有点hacky....但我真的很想知道应该如何做。肯定是有可能的吧?

无论如何,让我们开始吧:

    if (accessAllowed)
    {
        context.Succeed(requirement);
    }
    else
    {
        var mvcContext = (context.Resource as Microsoft.AspNetCore.Http.DefaultHttpContext);
        if (mvcContext != null)
        {
            mvcContext.Response.Redirect("/the-url-you-want-to-redirect-to");
        }           
    }

1

我采纳了herostwist的建议,但是策略可以挑战或禁止。经过深入研究,我发现可以像这样直接访问AuthorizationFilterContext(因为它们遵循命名约定并继承自AuthorizeAttribute):

public class BudgetAccessFilterAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        //context.HttpContext.User.Identity.Name
        //TODO: determine if user has access to budget controllers, all of them could inherit from a Common Controller with this Filter
        if (false)
        {
            //if no access then
            context.Result = new RedirectToActionResult("Index", "Home", null);
        }
    }     
}

您可以像这样装饰您的控制器:

[BudgetAccessFilter]
public class BudgetItemController : Controller
{

}

如果你有很多控制器需要进行相同的检查,那么它们可以都继承一个带有如下注释的基类:

[BudgetAccessFilter]
public class BCommonController : Controller
{
}

然后清理控制器:

public class BudgetItemController : BCommonController
{
} 

通过重新使用基于属性的授权,您将抵消基于策略的授权所带来的所有好处。当您有多个复杂的策略和角色时,此过滤器无法很好地处理。随着应用程序的增长,授权要求也会增加,可能有些操作需要诸如“必须年满18岁”的策略,而其中一些还需要额外的策略,例如“必须验证电子邮件”——您很快就会发现,基于属性的授权正在阻碍您前进。 - herostwist

-3

我正在使用cookie身份验证而不是Windows身份验证,但在我的Startup.cs中的Configure方法中,我有以下代码片段,告诉它要去哪里

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    LoginPath = "/account/login",
    AuthenticationScheme = "Cookies",
    AutomaticAuthenticate = true,
    AutomaticChallenge = true
});

这个解决方案只适用于cookie身份验证。 - Alex Hope O'Connor

-4

我不知道在哪里可以配置重定向结果,但至少我能够创建这样一个“Account/AccessDenied.cshtml”文件,在失败的情况下将显示该文件。 “Account”是我的类名,当失败发生时,浏览器会重定向到此网址:(http://localhost:39339/Account/AccessDenied?ReturnUrl=%2Fapp%2Fequipments)

这是我的控制器代码(Web/AccountController.cs)。

public class AccountController : Controller
{
    public IActionResult AccessDenied()
    {
        return View();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接