如何支持 NTLM 身份验证并回退到 ASP.NET MVC 中的表单?

Posted

技术标签:

【中文标题】如何支持 NTLM 身份验证并回退到 ASP.NET MVC 中的表单?【英文标题】:How to support NTLM authentication with fall-back to form in ASP.NET MVC? 【发布时间】:2011-04-30 23:39:14 【问题描述】:

如何在 ASP.NET MVC 应用程序中实现以下功能:

    用户打开内网网站 尽可能对用户进行静默身份验证 如果 NTLM 身份验证失败,向用户显示登录表单 用户指定登录密码并从预定义域列表中选择域 使用 AD 在代码中对用户进行身份验证

我知道如何实现 4 和 5,但找不到有关如何结合 NTLM 和表单的信息。 这样 NTLM 本机登录/密码对话框就永远不会显示 - 透明身份验证或漂亮的登录页面。

应该如何工作? 是否应该询问用户登录名和密码? 可以在不要求输入登录名和密码的情况下使用她当前的凭据(域用户名)吗?

更新这些,调查同样的问题:

当我问这个问题时,我并没有完全理解 NTLM 身份验证在内部是如何工作的。 这里要理解的重要一点是,如果用户的浏览器不能正确支持 NTLM,或者用户禁用了 NTLM 支持 - 服务器将永远没有机会解决这个问题。

Windows 身份验证的工作原理:

    客户端向服务器发送一个常规的 HTTP 请求 服务器响应 HTTP 状态 401 并指示必须使用 NTLM 身份验证来访问资源 客户端发送 NTLM Type1 消息 服务器以 NTLM Type2 消息响应挑战 客户端发送 Type3 消息以响应挑战 服务器响应请求的实际内容

如您所见,不支持 NTLM 的浏览器将不会转到步骤 (3),而是向用户显示 IIS 生成的错误 401 页面。

如果用户没有凭据,在取消 NTLM 身份验证后弹出对话框窗口浏览器也不会继续 (3)。

所以我们没有机会自动将用户重定向到自定义登录页面。

这里唯一的选择是有一个“网关”页面,我们决定用户是否应该支持 NTLM,如果支持,则重定向到受 NTLM 保护的主页。

如果没有,请显示登录表单并允许通过手动输入登录名和密码进行身份验证。

通常根据用户的 IP 地址和/或主机名通过匹配 IP 范围或检查预定义 IP 表来做出决定。

【问题讨论】:

***.com/questions/492977/… 【参考方案1】:

这篇文章可能会为您指明正确的方向。基本上,您在相同主机名下的两个虚拟目录中有两个应用程序。一个应用程序使用表单身份验证,一个应用程序使用 Windows。使用 Windows 身份验证的会创建一个有效的表单身份验证 cookie 并重定向到第二个虚拟目录。

ASP.NET Mixed Mode Authentication

【讨论】:

【参考方案2】:

我在生产中进行了这个精确的设置,我设置了我的门户以使用 FormsAuth 并编写了一个函数,该函数使用访问者 IP 来查找登录到该 IP/PC 的用户帐户。使用我找到的名称(例如DOMAIN\user),我使用Membership.GetUser(<user>) 验证域与我的域匹配并且用户名/帐户在我的FormsAth 提供程序中有效。如果此调用返回匹配并且用户 IsApproved 我为用户创建一个 FormsAuthenticationTicket & cookie。我有 400 多人在网络上,并且运行良好,唯一仍然登录的计算机是(1. 在我的门户中没有帐户的用户,2. 一些 MAC/Linux 用户,3. 没有在网络上启动的移动用户并让组策略将其防火墙启用为高)。

此解决方案的关键在于它需要模拟域管理员帐户来查询用户 PC,并且您使用非托管代码 netapi32.dll。 p>

这是我使用的代码(为简洁起见,未提供外部函数调用)。我试着简化一下,因为有很多外部调用。

string account = String.Empty;
string domain = String.Empty;
string user = String.Empty;


ImpersonateUser iu = new ImpersonateUser();  //Helper that Enabled Impersonation
if (iu.impersonateValidUser(StringHelper.GetAppSetting("DomainAccount"), StringHelper.GetAppSetting("DomainName"), StringHelper.GetEncryptedAppSetting("DomainAccountPassword")))

    NetWorkstationUserEnum nws = new NetWorkstationUserEnum(); //Wrapper for netapi32.dll (Tested on Vista, XP, Win2K, Win2K3, Win2K8)
    string host = nws.DNSLookup(Request.UserHostAddress); // netapi32.dll requires a host name, not an IP address

    string[] users = nws.ScanHost(host); // Gets the users/accounts logged in

    if (nws.ScanHost(host).Length > 0)
    
        string workstationaccount = string.Empty;

        if (host.IndexOf('.') == -1)  // Pick which account to use, I have 99.9% success with this logic (only time doesn't work is when you run a interactive process as a admin e.g. Run As <process>).
        
            workstationaccount = String.Format("0\\1$",StringHelper.GetAppSetting("DomainName"), host).ToUpper();
        
        else
        
            workstationaccount = String.Format("0\\1$", StringHelper.GetAppSetting("DomainName"), host.Substring(0, host.IndexOf('.'))).ToUpperInvariant();
        

        account = users[users.Length - 1].Equals(workstationaccount) ? users[0] : users[users.Length - 1];

        domain = account.Substring(0, account.IndexOf("\\"));
        user = account.Substring(account.IndexOf("\\") + 1,
                                 account.Length - account.IndexOf("\\") - 1);
    

    iu.undoImpersonation(); // Disable Impersonation

现在使用我们在第一个函数/进程中获取的帐户,我们现在尝试验证并决定是否应该向用户显示登录或自动登录。

MembershipUser membershipUser = Membership.GetUser(user);

if (membershipUser != null && membershipUser.IsApproved)

    string userRoles = string.Empty;  // Get all their roles
    FormsAuthenticationUtil.RedirectFromLoginPage(user, userRoles, true); // Create FormsAuthTicket + Cookie + 

我很久以前写了一篇关于这个的博客文章,这里是我在帖子Source Code Download 中提供的 netapi32.dll 的包装器和我的模拟助手的链接

【讨论】:

感谢您的详细回复,我还没有将其标记为答案,因为我觉得它可能与安全性有关,并且总体上有“黑客”的感觉 :-)【参考方案3】:

您不能在同一个 ASP.NET 应用程序中同时拥有 NTLM 和 FormsAuthentication。您将需要两个不同的应用程序位于不同的虚拟目录中。

【讨论】:

不完全正确 - 您无法配置(使用 web.config)应用程序以支持 NTLM 和 Forms 身份验证,但没有理由不能创建自定义身份验证模块来处理它。

以上是关于如何支持 NTLM 身份验证并回退到 ASP.NET MVC 中的表单?的主要内容,如果未能解决你的问题,请参考以下文章

如果需要,如何编写调用 WCF 服务并从 Kerberos 回退到 NTLM 的代码?

Three.js 检测 webgl 支持并回退到常规画布

读取本地文件流并回退到其他源

JWT 身份验证回退到相同路径的 SAML2

Django 1.11-如何使用仅支持NTLM身份验证的邮件服务器发送邮件

WCF - 如何为 NTLM 身份验证配置 netTcpBinding?