如何在Asp.Net Core 2.2中运行时禁用/启用身份验证?

3

网站默认只允许匿名访问。

管理员有一个按钮可以将网站切换到维护模式,这应该启用使用内置 CookieAuthentication 的授权(在数据库中翻转一个位,与本帖子无关)。

为了使其正常工作,我首先配置了 cookie 身份验证(在 startup.cs 中):

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
                options =>
                {
                    options.LoginPath = new PathString("/auth/login");
                });
} 

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   app.UseAuthentication();
}

然后在相关的控制器上,我加上了[Authorize]属性。

[Authorize]
public class HomeController : Controller
{
  //removed
}

这很完美 - 当authorize属性存在时,cookie认证会生效。目前为止一切顺利。

现在我想在运行时禁用授权,当维护模式关闭时。

尝试的解决方案

在经过大量试错和研究后,这就是我最终得出的结果。

public void OnAuthorization(AuthorizationFilterContext context)
    {
        IMaintenanceModeDataService ds = context.HttpContext.RequestServices.GetService<IMaintenanceModeDataService>();

        if (!ds.IsMaintenanceModeEnabled)
        {
            //Maintenance mode is off, no need for authorization
            return;
        }
        else
        {
            ClaimsPrincipal user = context.HttpContext.User;
            if (user.Identity.IsAuthenticated)
            {
                //when the user is authenticated, we don't need to do anything else.
                return;
            }
            else
            {
                //we're in maintenance mode AND the user is not 
                //It is outside the scope of this to redirect to a login
                //We just want to display maintenancemode.html
                context.Result = new RedirectResult("/maintenancemode.html");
                return;
            }
        }
    }

[MaintenanceModeAwareAuthorize]
public class HomeController : Controller
{
  //removed
}

当网站处于维护模式时,这很有效。

当网站未处于维护模式时,cookie身份验证仍然会启动并需要身份验证。我可以删除它并尝试实现自己的身份验证,但这是愚蠢的,因为我们已经内置了完美精良的解决方案。

如何在网站未处于维护模式(运行时)禁用授权?

备注:

问:为什么不通过执行x(需要访问配置、环境变量、服务器或类似内容)来处理此问题?

答:因为非技术管理员用户需要通过后端中的按钮立即访问此功能。

3个回答

8

当然可以!

ASP.NET Core的授权系统是可扩展的,您可以使用基于策略的授权轻松实现自己的场景。

要开始使用,需要了解两个主要内容:

  • 授权策略由一个或多个要求组成
  • 所有要求都必须满足,才能成功执行策略

因此,我们的目标是创建一个要求,如果以下任何一项为真,则满足要求:

  • 未启用维护模式,或
  • 用户已通过身份验证

让我们看看代码!

第一步是创建我们的要求:

public class MaintenanceModeDisabledOrAuthenticatedUserRequirement : IAuthorizationRequirement
{
}

我们接下来需要实现这个需求的处理程序,它将确定是否满足需求。好消息是,处理程序支持依赖注入:
public class MaintenanceModeDisabledOrAuthenticatedUserRequirementHandler : AuthorizationHandler<MaintenanceModeDisabledOrAuthenticatedUserRequirement>
{
    private readonly IMaintenanceModeDataService _maintenanceModeService;

    public MaintenanceModeDisabledOrAuthenticatedUserRequirementHandler(IMaintenanceModeDataService maintenanceModeService)
    {
        _maintenanceModeService = maintenanceModeService;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MaintenanceModeDisabledOrAuthenticatedUserRequirement requirement)
    {
        if (!_maintenanceModeService.IsMaintenanceModeEnabled || context.User.Identities.Any(x => x.IsAuthenticated))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

接下来,我们需要创建一个授权策略来使用此要求,您有两个选择:

  • 您可以重新定义默认策略,当使用“空”的[Authorize]属性时使用,或者
  • 创建一个显式策略,您将不得不在属性中引用它,例如[Authorize(Policy = "<your-policy-name>")]

没有正确或错误的答案;如果我的应用程序只有一个授权策略,我会选择第一种选项,如果有多个授权策略,我会选择第二种选项。我们将看到如何执行这两种方法:

services
    .AddAuthorization(options =>
    {
        // 1. This is how you redefine the default policy
        // By default, it requires the user to be authenticated
        //
        // See https://github.com/dotnet/aspnetcore/blob/30eec7d2ae99ad86cfd9fca8759bac0214de7b12/src/Security/Authorization/Core/src/AuthorizationOptions.cs#L22-L28
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .AddRequirements(new MaintenanceModeDisabledOrAuthenticatedUserRequirement())
            .Build();

        // 2. Define a specific, named policy that you can reference from your [Authorize] attributes
        options.AddPolicy("MaintenanceModeDisabledOrAuthenticatedUser", builder => builder
            .AddRequirements(new MaintenanceModeDisabledOrAuthenticatedUserRequirement()));
    });

接下来,您需要将需求处理程序注册为IAuthorizationHandler,如官方文档所示。

// The lifetime you pick is up to you
// You just need to remember that it's got a dependency on IMaintenanceModeDataService, so if you
// registered the implementation of IMaintenanceModeDataService as a scoped service, you shouldn't
// register the handler as a singleton
// See this captive dependency article from Mark Seeman: https://blog.ploeh.dk/2014/06/02/captive-dependency/
services.AddScoped<IAuthorizationHandler, MaintenanceModeDisabledOrAuthenticatedUserRequirementHandler>();

最后一步是根据需要在您的控制器/操作上应用[Authorize]属性。
// 1. If you redefined the default policy
[Authorize]
public IActionResult Index()
{
    return View();
}

// 2. If you defined an explicit policy
[Authorize(Policy = "MaintenanceModeDisabledOrAuthenticatedUser")]
public IActionResult Index()
{
    return View();
}

一个无关的评论,但我认为很重要的是,自2019年12月23日起,微软不再支持ASP.NET Core 2.2;请参见https://dotnet.microsoft.com/platform/support/policy/dotnet-core#lifecycle。我建议将目标定在ASP.NET Core 2.1,这是一个长期支持(LTS)版本,或考虑升级到ASP.NET Core 3.1。 - Mickaël Derriey
非常深入的回答!谢谢! - Kjensen
非常有用的答案!对于我的情况,我想要完全禁用某些API测试的身份验证。基于此,我简化了处理程序,只需成功处理要求即可:context.Succeed(requirement); return Task.CompletedTask; - Adrian Hristov

1

很抱歉,这个无法完成。授权的接受与身份验证不同,当 context.HttpContext.User.Identity.IsAuthenticatedfalse 时,它将总是重定向到登录页面。

最好将必须或可能需要授权的操作放在一个控制器中,将未经授权的操作放在另一个带有 [AllowAnonymous] 的控制器中。

if (!user.IsMaintenanceModeEnabled)
{
    context.Result = new RedirectResult("Another controller with [AllowAnonymous]");
     return;
}

0

由于当前页面需要与匿名模式完美配合,因此身份验证不应在控制器级别进行。

我认为您的请求是:

  1. 如果有Maintancer登录系统,

    • 运行额外的代码以显示页面上的维护元素(切换按钮或其他元素),以便Maintancer可以使用不同的模式切换页面,并执行维护操作
  2. 如果用户以匿名方式访问网站,则匿名模式元素将呈现到浏览器中
  3. 如果用户登录但不是Maintancer,则普通用户模式元素将呈现到浏览器中

要解决这些问题,关键是阻止未经授权的用户访问Maintancer操作,而不是控制器

我的建议是:

在_Layout.cshtml页面中,检查是否有"Maintancer Login",如果是,则插入"switch button"。
在可以匿名访问的动作或页面中,检查是否存在"Maintancer Login"且IsMaintenanceMode为真,然后显示"Maintancer-authorized elements",例如"Delete Post"、"Edit Content"等。
在Controller的Actions中,仅针对"Maintancer"(例如"Delete Post")的操作,添加[Authorize(Roles="Maintancer")]或[Authorize(Policy="Maintancer")]或您自定义的授权方式。

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