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

C# Windows身份验证-特殊用户需要额外密码,c#,asp.net-core,windows-authentication,C#,Asp.net Core,Windows Authentication,我正在开发一个内部网asp.net核心web api应用程序。认证要求如下: REQ1-当试图访问网站的用户不在Active Directory的特殊组(让我们将其命名为“commonUsers”)中时,它就是没有授权的 REQ2-当试图访问网站的用户在Active Directory的组中时,“commonUsers”被授权并返回web资源 REQ3-当试图访问网站的用户位于Active Directory的组“超级用户”中时,需要再次提示其输入域密码(因为它试图访问一些非常受限的资源)

我正在开发一个内部网asp.net核心web api应用程序。认证要求如下:

  • REQ1-当试图访问网站的用户不在Active Directory的特殊组(让我们将其命名为“commonUsers”)中时,它就是没有授权的
  • REQ2-当试图访问网站的用户在Active Directory的组中时,“commonUsers”被授权并返回web资源
  • REQ3-当试图访问网站的用户位于Active Directory的组“超级用户”中时,需要再次提示其输入域密码(因为它试图访问一些非常受限的资源)
现在,我所知道的是:

  • 我的服务使用http.sys服务器托管,以支持windows身份验证
  • 我使用声明是为了检查用户的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);
    }}
    
    public类claimtransformer:iclaims转换{
    私有只读IAuthorizationService\u授权服务;
    公开请求传输格式(IAAuthorizationService授权服务)
    {
    _授权服务=授权服务;
    }
    公共任务TransformAsync(ClaimsPrincipal主体)
    {
    _授权服务。授权(委托人作为IHMICLAIMS委托人);
    返回任务.FromResult(主体);
    }}
    
  • 我在服务配置中也指定了特殊策略,例如:

    services.AddAuthorization(options => { options.AddPolicy("TestPolicy", policy => policy.RequireClaim(ClaimTypes.Role, "TestUser")); options.AddPolicy("TestPolicy2", policy => policy.RequireClaim(ClaimTypes.Role, "SuperUser")); }); services.AddAuthorization(选项=> { options.AddPolicy(“TestPolicy”,policy=> policy.requirecall(ClaimTypes.Role,“TestUser”); options.AddPolicy(“TestPolicy2”,policy=> policy.requirecall(ClaimTypes.Role,“超级用户”); });
  • 我正在使用带有特定策略的
    [Authorize]
    属性,以便根据策略限制对特定资源的访问


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

我猜您正在尝试对
部分资源实施两步身份验证
为此,您必须使用多个
身份验证方案
授权策略
, 但这很困难,因为windows身份验证是不可控的。我们需要使用一些技巧来知道这是你的第二次登录

认证
  • 默认的身份验证方案:
    Windows
    ,它是对Windows用户进行身份验证的基本方案
  • 第二个
    Cookies
    基本身份验证方案:
    SuperUserTwoStep
    。我们需要这个来转到我们的自定义登录逻辑
  • 授权
  • 指定方案的
    授权策略
  • 用于登录到
    SuperUserTwoStep
    方案的登录页面

  • <>最困难的事情是跟踪这是第二次登录,我尝试使用Cookie,但是它不工作,所以我装箱一个<代码>静态的自动机< /代码>跟踪,<强>也许使用分布式缓存更好的< /强>

    < p>我认为你应该考虑使用:基于策略的授权与需求,基本上,您有不同的授权要求,希望根据这些要求进行处理

    需求1、需求2和需求3

    这里有指向文档的链接:

    但你需要了解这种身份权限,那些向微软介绍策略概念的人创建了一个名为:PolicyServer的项目,它是开源的:他们在那里创建了一个你应该如何使用策略的模式。基本上,您的外部和内部用户都是根据您的广告进行身份验证的,所有内部用户都应该具有分配给角色的权限。并且您只使用为该策略创建的权限规则来修饰控制器操作

    [Authorize("PerformSurgery")]
    public async Task<IActionResult> PerformSurgery()
    {
        // omitted
    }
    
    [授权(“执行外科手术”)]
    公共异步任务PerformSurgery()
    {
    //省略
    }
    
    为了理解代码以及他们如何评估策略,我认为您应该查看他们在网站上的在线视频:


    希望这有帮助

    我想我会尝试使用MVC过滤器:

    过滤器在所有中间件之后但在操作之前运行。这将允许您仅为特定操作或控制器控制重定向到凭据页面。虽然通常这不是推荐的授权方法,但我认为它符合您对混合二级身份验证的要求

    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
        }
    }
    

    如果您的web应用程序故意返回401响应,浏览器应该提示。IE也是这样吗?嗯,您没有尝试吗?@niao您是否尝试对超级用户实施两步身份验证?@John-这似乎是我尝试使用Windows身份验证来做的。我会检查这一点。这似乎有效,但是,当我想要访问特定的资源时,例如超级用户可以访问的/api/TestController/SomeAction,应该再次提示输入凭据(在这种情况下,PUT和GET操作是可禁用的)和其他类型的用户(不应该再次提示输入凭据),会出现什么情况-在这种情况下,只读资源可用(获取操作)?@niao在这种情况下,您可以定义一个和一个
    处理程序来自己处理authorze策略。PS:也可以尝试使用<代码> XHR <代码>访问“两步”API,不确定它是否会显示凭据弹出窗口。@ NIAO还考虑了<代码> @ StfU/<代码>的建议,使用其他验证方法来确认两步授权(例如使用SMS或电子邮件发送的确认代码,甚至显示在屏幕上),windows登录太难控制了我会检查的
    
    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
        }
    }