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

Kje*_*sen 3 c# asp.net-core-mvc asp.net-core asp.net-core-2.2

网站默认只能匿名访问。

管理员有一个按钮可以将站点切换到维护模式,这应该使用内置的 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();
}
Run Code Online (Sandbox Code Playgroud)

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

[Authorize]
public class HomeController : Controller
{
  //removed
}
Run Code Online (Sandbox Code Playgroud)

这工作得很好——当授权属性存在时,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
}
Run Code Online (Sandbox Code Playgroud)

当站点处于维护模式时,这非常有用。

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

当站点不处于维护模式(运行时)时,如何禁用授权?

笔记:

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

答:因为非技术管理员用户需要通过单击后端的按钮立即访问它。

Mic*_*iey 8

是的你可以!

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

开始之前需要了解两个主要事项:

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

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

让我们看看代码!

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

public class MaintenanceModeDisabledOrAuthenticatedUserRequirement : IAuthorizationRequirement
{
}
Run Code Online (Sandbox Code Playgroud)

然后我们必须实现此要求的处理程序,这将确定它是否满足。好消息是处理程序支持依赖注入:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以重新定义使用“空”属性时使用的默认策略[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()));
    });
Run Code Online (Sandbox Code Playgroud)

接下来,您需要将需求处理程序注册为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>();
Run Code Online (Sandbox Code Playgroud)

[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();
}
Run Code Online (Sandbox Code Playgroud)