【问题标题】:How do I disable/enable authentication at runtime in Asp.Net Core 2.2?如何在 Asp.Net Core 2.2 运行时禁用/启用身份验证?
【发布时间】:2020-06-18 08:22:11
【问题描述】:

默认情况下,网站只能匿名访问。

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

这非常有效——当授权属性存在时,cookie auth 就会启动。到目前为止一切顺利。

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

尝试的解决方案

这是我经过大量反复试验和研究后得出的结论。

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 来处理这个问题(这需要服务器端访问配置、环境变量、服务器或类似内容)?

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

【问题讨论】:

    标签: c# asp.net-core asp.net-core-mvc asp.net-core-2.2


    【解决方案1】:

    是的,你可以!

    ASP.NET Core 中的授权系统是可扩展的,您可以使用poliy-based authorization 轻松实现您的场景。

    开始前要知道的两个主要事项:

    • 授权策略由一个或多个requirements组成
    • 必须满足所有要求才能使策略成功

    我们的目标是创建一个满足以下任一陈述的需求:

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

    让我们看看代码!

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

    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 = "&lt;your-policy-name&gt;")]

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

    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 official docs 所示

    // 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;见dotnet.microsoft.com/platform/support/policy/…。我建议以长期支持 (LTS) 版本的 ASP.NET Core 2.1 为目标,或者考虑升级到 ASP.NET Core 3.1。
    • 很好的深入回答!谢谢!
    • 非常有用的答案!就我而言,我想完全禁用某些 API 测试的身份验证。基于我的解决方案,但简化了处理程序以成功处理需求context.Succeed(requirement); return Task.CompletedTask;
    【解决方案2】:

    恐怕做不到。授权的接受与认证不同,当context.HttpContext.User.Identity.IsAuthenticatedfalse时,它总是重定向到登录页面。

    最好将必须或可能需要在控制器中授权的操作一起使用,而在单独的控制器中使用[AllowAnonymous] 进行未经授权的操作。

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

    【讨论】:

      【解决方案3】:

      由于当前页面需要在anonymous 模式下完美运行,因此身份验证不应处于Controller 级别。

      我认为您的要求是:

      1. 如果是Maintancer登录系统,

        • 运行额外代码在页面上显示maintance elements(切换按钮或其他),以便Maintancer可以切换不同模式的页面,并执行maintancer actions
      2. 如果用户访问站点anonymouslyanonymous-mode elements 将呈现到浏览器
      3. 如果用户登录但不是Maintancernormal-user-mode elements 将呈现到浏览器

      要解决这些问题,关键是阻止未经授权的用户访问Maintancer ACTIONS,而不是controller

      我的建议是:

      1. 在_Layout.cshtml页面中,检查是否Maintancer Login,然后注入switch button
      2. 在可能匿名访问的操作或页面中,检查是否“维护者登录”&& IsMaintenanceMode,然后显示Maintancer-authorized elements,如Delete PostEdit Content、...
      3. 在仅适用于Maintancer(如Delete Post)的Controller.Actions 中,添加[Authorize(Roles="Maintancer")][Authorize(Policy="Maintancer")] 或自定义授权。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-06
        • 2020-03-21
        • 1970-01-01
        • 2014-07-12
        • 1970-01-01
        • 2019-10-18
        • 2019-12-13
        • 2017-11-30
        相关资源
        最近更新 更多