Windows 身份验证 - 特殊用户需要额外的密码

Windows Authentication - require additional password for special users

我正在开发一个 Intranet asp.net 核心网络 api 应用程序。身份验证的要求是:

现在,我目前拥有的:

现在的问题是,我应该如何满足REQ3?

我猜您正在尝试为 some of your resource 实施两步验证。
为此,您必须使用多个 authentication schemeAuthorize policies, 但这很困难,因为 windows 身份验证不可控。我们需要使用一些技巧才能知道这是您的第二次登录。

身份验证

  1. 默认身份验证方案:Windows,这是验证 windows 用户的基本方案。
  2. 第二个 Cookies 基本身份验证方案:SuperUserTwoStep。我们需要这个来转到我们的自定义登录逻辑。

授权

  1. 指定方案的 Authorize policies
  2. 用于登录 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
    }
}