在 Asp.Net Core 2.2 中如何在运行时进行 disable/enable 身份验证?
How do I disable/enable authentication at runtime in Asp.Net Core 2.2?
网站默认只能匿名访问。
管理员有一个按钮可以将站点切换到维护模式,该按钮应该使用内置的 CookieAuthentication 启用授权(在数据库中稍微翻转一下,与此无关post)。
为了让它工作,我首先配置了 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 身份验证在存在授权属性时启动。到目前为止一切顺利。
现在我想在维护模式关闭时禁用授权在运行时。
尝试的解决方案
这是我经过大量试验、错误和研究后得出的结论。
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 来处理这个问题(这需要服务器端访问配置、环境变量、服务器或类似的)?
答:因为非技术管理员用户需要通过单击后端中的按钮立即访问它。
恐怕做不到。接受授权不同于认证,当context.HttpContext.User.Identity.IsAuthenticated
为false
时,它总是重定向到登录页面。
最好将必须或可能需要授权的操作放在一个控制器中,将未经授权的操作放在单独的控制器中 [AllowAnonymous]
。
if (!user.IsMaintenanceModeEnabled)
{
context.Result = new RedirectResult("Another controller with [AllowAnonymous]");
return;
}
由于当前页面需要在 anonymous
模式下完美工作,因此身份验证不应处于 Controller
级别。
我认为你的要求是:
如果Maintancer
登录系统,
- 运行 在页面上显示
maintance elements
(切换按钮或其他)的额外代码,因此 Maintancer
可以使用不同的模式切换页面,并执行 maintancer actions
- 如果用户访问站点
anonymously
,anonymous-mode elements
将呈现给浏览器
- 如果用户登录但不是
Maintancer
,normal-user-mode elements
将呈现给浏览器
要解决这些问题,关键是要阻止未经授权的用户访问 Maintancer ACTIONS
,而不是 controller
。
我的建议是:
- 在_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")]
或自定义授权。
是的,你可以!
ASP.NET 核心中的授权系统是可扩展的,您可以使用 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;
}
}
接下来,我们需要创建一个使用此要求的授权策略,您在这里有 2 个选择:
- 您可以重新定义默认策略,在使用 "empty"
[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 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>();
最后一步是根据需要在 controllers/actions 上应用 [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();
}
网站默认只能匿名访问。
管理员有一个按钮可以将站点切换到维护模式,该按钮应该使用内置的 CookieAuthentication 启用授权(在数据库中稍微翻转一下,与此无关post)。
为了让它工作,我首先配置了 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 身份验证在存在授权属性时启动。到目前为止一切顺利。
现在我想在维护模式关闭时禁用授权在运行时。
尝试的解决方案
这是我经过大量试验、错误和研究后得出的结论。
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 来处理这个问题(这需要服务器端访问配置、环境变量、服务器或类似的)?
答:因为非技术管理员用户需要通过单击后端中的按钮立即访问它。
恐怕做不到。接受授权不同于认证,当context.HttpContext.User.Identity.IsAuthenticated
为false
时,它总是重定向到登录页面。
最好将必须或可能需要授权的操作放在一个控制器中,将未经授权的操作放在单独的控制器中 [AllowAnonymous]
。
if (!user.IsMaintenanceModeEnabled)
{
context.Result = new RedirectResult("Another controller with [AllowAnonymous]");
return;
}
由于当前页面需要在 anonymous
模式下完美工作,因此身份验证不应处于 Controller
级别。
我认为你的要求是:
如果
Maintancer
登录系统,- 运行 在页面上显示
maintance elements
(切换按钮或其他)的额外代码,因此Maintancer
可以使用不同的模式切换页面,并执行maintancer actions
- 运行 在页面上显示
- 如果用户访问站点
anonymously
,anonymous-mode elements
将呈现给浏览器 - 如果用户登录但不是
Maintancer
,normal-user-mode elements
将呈现给浏览器
要解决这些问题,关键是要阻止未经授权的用户访问 Maintancer ACTIONS
,而不是 controller
。
我的建议是:
- 在_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")]
或自定义授权。
是的,你可以!
ASP.NET 核心中的授权系统是可扩展的,您可以使用 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;
}
}
接下来,我们需要创建一个使用此要求的授权策略,您在这里有 2 个选择:
- 您可以重新定义默认策略,在使用 "empty"
[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 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>();
最后一步是根据需要在 controllers/actions 上应用 [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();
}