Windows 身份验证 - 特殊用户需要额外的密码
Windows Authentication - require additional password for special users
我正在开发一个 Intranet asp.net 核心网络 api 应用程序。身份验证的要求是:
- REQ1 - 当试图访问该网站的用户不在 Active Directory 的特殊组中(我们将其命名为 "commonUsers")时,它根本没有被授权
- REQ2 - 当试图访问该网站的用户在 Active Directory 组中时 "commonUsers" 获得授权并返回 Web 资源
- REQ3 - 当试图访问该网站的用户在 Active Directory 组 "superUser" 中时,需要再次提示他输入他的域密码(因为它试图访问一些非常受限的资源)
现在,我目前拥有的:
- 我的服务使用 http.sys 服务器托管,以支持 windows 身份验证。
我正在使用 claims transformer middlewere 来检查用户的 Active Directory 组,让我们这样说:
public class ClaimsTransformer : IClaimsTransformation {
private readonly IAuthorizationService _authorizationService;
public ClaimsTransformer(IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
_authorizationService.Authorize(principal as IHmiClaimsPrincipal);
return Task.FromResult(principal);
}}
我在我的服务配置中也指定了一个特殊的策略,例如:
services.AddAuthorization(options =>
{
options.AddPolicy("TestPolicy", policy =>
policy.RequireClaim(ClaimTypes.Role, "TestUser"));
options.AddPolicy("TestPolicy2", policy =>
policy.RequireClaim(ClaimTypes.Role, "SuperUser"));
});
我正在使用具有特定策略的 [Authorize]
属性,以便根据策略限制对特定资源的访问
现在的问题是,我应该如何满足REQ3?
我猜您正在尝试为 some of your resource
实施两步验证。
为此,您必须使用多个 authentication scheme
和 Authorize policies
,
但这很困难,因为 windows 身份验证不可控。我们需要使用一些技巧才能知道这是您的第二次登录。
身份验证
- 默认身份验证方案:
Windows
,这是验证 windows 用户的基本方案。
- 第二个
Cookies
基本身份验证方案:SuperUserTwoStep
。我们需要这个来转到我们的自定义登录逻辑。
授权
- 指定方案的
Authorize policies
。
- 用于登录
SuperUserTwoStep
方案的登录页面。
//startup
services.AddAuthentication(HttpSysDefaults.AuthenticationScheme)
.AddCookie("SuperUserTwoStep",op=>op.LoginPath = "/account/superuser2steplogin");
services.AddAuthorization(op =>
{
op.AddPolicy("SuperUser", b => b.AddAuthenticationSchemes("SuperUserTwoStep")
.RequireAuthenticatedUser()
.RequireClaim(ClaimTypes.Role, "SuperUser"));
});
// login
public static IDictionary<string, string> States { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
[Route("/account/superuser2steplogin")]
public async Task<IActionResult> LoginTwoStepConfirm(string returnUrl, [FromServices]IAuthorizationService authorizationService,
[FromServices]IAuthorizationPolicyProvider policyProvider)
{
var winresult = await HttpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme);
if (winresult.Succeeded)
{
if (States.TryGetValue(winresult.Principal.Identity.Name, out _))
{
States.Remove(winresult.Principal.Identity.Name);
var principal = new System.Security.Claims.ClaimsPrincipal(new System.Security.Claims.ClaimsIdentity(winresult.Principal.Claims,"twostepcookie"));
await HttpContext.SignInAsync("SuperUserTwoStep", principal);
return Redirect(returnUrl);
}
else
{
States[winresult.Principal.Identity.Name] = "1";
return Challenge(IISDefaults.AuthenticationScheme);
}
}
else
{
return Challenge(IISDefaults.AuthenticationScheme);
}
}
[Authorize("SuperUser")]
public IActionResult YourSecurePage()
{
return Content("hello world");
}
最难跟踪的是这是第二次登录,我尝试用cookie,但是不行,所以我crate了一个static IDitionary<string,string>
来跟踪,也许使用分布式缓存更好
我认为在我看来你应该考虑使用:Policy-based 授权与要求,基本上你有不同的授权要求,你想在 AND 基础上处理它们
REQ1、REQ2 和 REQ3
这里有文档的 link:Requirements
但您需要了解身份!= 权限,将此策略概念引入 Microsoft 的人员创建了一个名为:PolicyServer 的项目,它是开源的:PolicyServer Git 他们在那里创建了一个模式你应该使用你的政策。基本上,您有针对您的 AD 进行身份验证的外部和内部用户,所有内部用户都应具有分配给角色的权限。你只用你为该策略创建的权限规则装饰你的控制器操作
[Authorize("PerformSurgery")]
public async Task<IActionResult> PerformSurgery()
{
// omitted
}
要了解代码以及他们如何评估政策,我认为您应该观看他们在网站上在线提供的视频:Policy Server
希望对您有所帮助
我想我会尝试使用 MVC 过滤器:https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#authorization-filters
过滤 运行 在所有中间件之后,但在操作之前。这将允许您控制仅针对特定操作或控制器重定向到凭据页面。虽然通常这不是推荐的授权方法,但我认为它符合您对混合辅助身份验证的要求。
public class SuperUserFilter : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context.HttpContext.Request.Cookies.TryGetValue("SuperUserCookie", out string cookieVal))
{
if (!IsValidCookie(cookieVal))
context.Result = LoginPage(context);
}
else
{
context.Result = LoginPage(context);
}
}
private bool IsValidCookie(string cookieVal)
{
//validate cookie value somehow
// crytpographic hash, store value in session, whatever
return true;
}
private ActionResult LoginPage(AuthorizationFilterContext context)
{
return new RedirectToActionResult("SuperUser", "Login",
new {redirectUrl = context.HttpContext.Request.GetEncodedUrl()});
}
}
然后你创建一个登录控制器
public class LoginController : Controller
{
[HttpGet]
public IActionResult SuperUser(string redirectUrl)
{
// return a page to enter credentials
// Include redirectUrl as field
}
[HttpPost]
public IActionResult SuperUser(LoginData loginData)
{
// Validate User & Password
Response.Cookies.Append("SuperUserCookie", "SomeValue");
return Redirect(loginData.RedirectUrl);
}
}
然后你可以根据需要装饰具体的动作(或控制器):
public class MyController : Controller
{
[HttpGet]
[SuperUserFilter]
public IActionResult MySensitiveAction()
{
// Do something sensitive
}
}
我正在开发一个 Intranet asp.net 核心网络 api 应用程序。身份验证的要求是:
- REQ1 - 当试图访问该网站的用户不在 Active Directory 的特殊组中(我们将其命名为 "commonUsers")时,它根本没有被授权
- REQ2 - 当试图访问该网站的用户在 Active Directory 组中时 "commonUsers" 获得授权并返回 Web 资源
- REQ3 - 当试图访问该网站的用户在 Active Directory 组 "superUser" 中时,需要再次提示他输入他的域密码(因为它试图访问一些非常受限的资源)
现在,我目前拥有的:
- 我的服务使用 http.sys 服务器托管,以支持 windows 身份验证。
我正在使用 claims transformer middlewere 来检查用户的 Active Directory 组,让我们这样说:
public class ClaimsTransformer : IClaimsTransformation { private readonly IAuthorizationService _authorizationService; public ClaimsTransformer(IAuthorizationService authorizationService) { _authorizationService = authorizationService; } public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) { _authorizationService.Authorize(principal as IHmiClaimsPrincipal); return Task.FromResult(principal); }}
我在我的服务配置中也指定了一个特殊的策略,例如:
services.AddAuthorization(options => { options.AddPolicy("TestPolicy", policy => policy.RequireClaim(ClaimTypes.Role, "TestUser")); options.AddPolicy("TestPolicy2", policy => policy.RequireClaim(ClaimTypes.Role, "SuperUser")); });
我正在使用具有特定策略的
[Authorize]
属性,以便根据策略限制对特定资源的访问
现在的问题是,我应该如何满足REQ3?
我猜您正在尝试为 some of your resource
实施两步验证。
为此,您必须使用多个 authentication scheme
和 Authorize policies
,
但这很困难,因为 windows 身份验证不可控。我们需要使用一些技巧才能知道这是您的第二次登录。
身份验证
- 默认身份验证方案:
Windows
,这是验证 windows 用户的基本方案。 - 第二个
Cookies
基本身份验证方案:SuperUserTwoStep
。我们需要这个来转到我们的自定义登录逻辑。
授权
- 指定方案的
Authorize policies
。 - 用于登录
SuperUserTwoStep
方案的登录页面。
//startup
services.AddAuthentication(HttpSysDefaults.AuthenticationScheme)
.AddCookie("SuperUserTwoStep",op=>op.LoginPath = "/account/superuser2steplogin");
services.AddAuthorization(op =>
{
op.AddPolicy("SuperUser", b => b.AddAuthenticationSchemes("SuperUserTwoStep")
.RequireAuthenticatedUser()
.RequireClaim(ClaimTypes.Role, "SuperUser"));
});
// login
public static IDictionary<string, string> States { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
[Route("/account/superuser2steplogin")]
public async Task<IActionResult> LoginTwoStepConfirm(string returnUrl, [FromServices]IAuthorizationService authorizationService,
[FromServices]IAuthorizationPolicyProvider policyProvider)
{
var winresult = await HttpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme);
if (winresult.Succeeded)
{
if (States.TryGetValue(winresult.Principal.Identity.Name, out _))
{
States.Remove(winresult.Principal.Identity.Name);
var principal = new System.Security.Claims.ClaimsPrincipal(new System.Security.Claims.ClaimsIdentity(winresult.Principal.Claims,"twostepcookie"));
await HttpContext.SignInAsync("SuperUserTwoStep", principal);
return Redirect(returnUrl);
}
else
{
States[winresult.Principal.Identity.Name] = "1";
return Challenge(IISDefaults.AuthenticationScheme);
}
}
else
{
return Challenge(IISDefaults.AuthenticationScheme);
}
}
[Authorize("SuperUser")]
public IActionResult YourSecurePage()
{
return Content("hello world");
}
最难跟踪的是这是第二次登录,我尝试用cookie,但是不行,所以我crate了一个static IDitionary<string,string>
来跟踪,也许使用分布式缓存更好
我认为在我看来你应该考虑使用:Policy-based 授权与要求,基本上你有不同的授权要求,你想在 AND 基础上处理它们
REQ1、REQ2 和 REQ3
这里有文档的 link:Requirements
但您需要了解身份!= 权限,将此策略概念引入 Microsoft 的人员创建了一个名为:PolicyServer 的项目,它是开源的:PolicyServer Git 他们在那里创建了一个模式你应该使用你的政策。基本上,您有针对您的 AD 进行身份验证的外部和内部用户,所有内部用户都应具有分配给角色的权限。你只用你为该策略创建的权限规则装饰你的控制器操作
[Authorize("PerformSurgery")]
public async Task<IActionResult> PerformSurgery()
{
// omitted
}
要了解代码以及他们如何评估政策,我认为您应该观看他们在网站上在线提供的视频:Policy Server
希望对您有所帮助
我想我会尝试使用 MVC 过滤器:https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#authorization-filters
过滤 运行 在所有中间件之后,但在操作之前。这将允许您控制仅针对特定操作或控制器重定向到凭据页面。虽然通常这不是推荐的授权方法,但我认为它符合您对混合辅助身份验证的要求。
public class SuperUserFilter : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context.HttpContext.Request.Cookies.TryGetValue("SuperUserCookie", out string cookieVal))
{
if (!IsValidCookie(cookieVal))
context.Result = LoginPage(context);
}
else
{
context.Result = LoginPage(context);
}
}
private bool IsValidCookie(string cookieVal)
{
//validate cookie value somehow
// crytpographic hash, store value in session, whatever
return true;
}
private ActionResult LoginPage(AuthorizationFilterContext context)
{
return new RedirectToActionResult("SuperUser", "Login",
new {redirectUrl = context.HttpContext.Request.GetEncodedUrl()});
}
}
然后你创建一个登录控制器
public class LoginController : Controller
{
[HttpGet]
public IActionResult SuperUser(string redirectUrl)
{
// return a page to enter credentials
// Include redirectUrl as field
}
[HttpPost]
public IActionResult SuperUser(LoginData loginData)
{
// Validate User & Password
Response.Cookies.Append("SuperUserCookie", "SomeValue");
return Redirect(loginData.RedirectUrl);
}
}
然后你可以根据需要装饰具体的动作(或控制器):
public class MyController : Controller
{
[HttpGet]
[SuperUserFilter]
public IActionResult MySensitiveAction()
{
// Do something sensitive
}
}