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

发布于 2024-09-29 14:47:06 字数 1076 浏览 1 评论 0原文

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

  1. 用户打开 Intranet 网站
  2. 如果可能的话,
  3. 如果 NTLM 身份验证未成功,则对用户进行静默身份验证,向用户显示登录表单
  4. 用户指示登录密码并从预定义域列表中选择域
  5. 用户是使用 AD 在代码中进行身份验证

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

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

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

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

Windows 身份验证的工作原理:

  1. 客户端向服务器发送常规 HTTP 请求
  2. 服务器使用 HTTP 状态 401 进行响应,并指示必须使用 NTLM 身份验证才能访问资源
  3. 客户端发送 NTLM Type1 消息
  4. 服务器使用带有质询的 NTLM Type2 消息进行响应
  5. 客户端发送带有响应的 Type3 消息 服务器以请求
  6. 的实际内容进行响应

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

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

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

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

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

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

How can I implement following in ASP.NET MVC application:

  1. user opens intranet website
  2. user is silently authenticated if possible
  3. if NTLM authentication didn't worked out, show login form to user
  4. user indicate login password and select domain from list of predefined domains
  5. user is authenticated in code using AD

I know how to implement 4 and 5 but cannot find info on how to combine NTLM and forms.
So that NTLM native login/password dialog is never shown - transparent authentication or nice looking login page.

How should work?
Should user be asked login and password?
Can her current credentials (domain username) be used without asking to enter login and password?

UPDATE for these, investigating same problem:

When I was asking this I was not fully understand how NTLM authentication works internally.
Important thing here to understand is that if user's browser doesn't support NTLM properly or if NTLM support is disabled by user - server will never get chance to work around this.

How Windows authentication is working:

  1. Client send a regular HTTP request to server
  2. Server responds with HTTP status 401 and indication that NTLM authentication must be used to access resources
  3. Client send NTLM Type1 message
  4. Server responds with NTLM Type2 message with challenge
  5. Client send Type3 message with response to challenge
  6. Server responds with actual content requested

As you see, browser not supporting NTLM will not go to step (3), instead user will be shown IIS generated Error 401 page.

If user doesn’t have credentials, after cancelling NTLM authentication popup dialog window browser will not continue to (3) as well.

So we have no chance to automatically redirect users to custom login page.

The only option here is to have a “gateway” page where we decide if user should support NTLM and if so, redirect to NTLM protected home page.

And if not, show login form and allow authentication by manually entering login and password.

Decision is usually made based on users’ IP address and/or host name either by matching IP ranges or by checking table of predefined IPs.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

宛菡 2024-10-06 14:47:06

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

ASP.NET混合模式认证

This article might get you pointed in the right direction. Basically you have two apps in two virtual directories under the same host name. One app uses Forms authentication, one uses Windows. The one using Windows authentication creates a valid form authentication cookie and redirects to the second virtual directory.

ASP.NET Mixed Mode Authentication

别念他 2024-10-06 14:47:06

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

此解决方案的问题在于,它需要模拟域管理员帐户来查询用户的电脑,并且您使用非托管代码netapi32.dll

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

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

我很久以前写了一篇关于此的博客文章,这里是我在帖子中提供的 netapi32.dll 包装器和我的模拟助手的链接 源代码下载

I have this exact setup in production, I setup my portal to use FormsAuth and wrote a function that takes the visitors IP to look up the user account that is logged in to that IP / PC. Using the name I find (eg. DOMAIN\user), I verify the domain matches my domain and that the user name / account is valid in my FormsAth provider using Membership.GetUser(<user>). If this call returns a match and the user IsApproved I create a FormsAuthenticationTicket & cookie for the user. I have 400+ people on the network and this works perfectly, the only computers that still login are (1. Users without accounts in my portal, 2. A few MAC/Linux users, 3. Mobile users who did not boot on the network and had Group Policy enable their Firewall to High).

The catch to this solution is that it requires impersonation of a domain admin account to query the users PC, and that you use unmanaged code netapi32.dll.

Here is the code I use (external function calls not provided, for brevity). I've tried to simplify this a bit, since have LOTS of external calls.

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}
quot;,StringHelper.GetAppSetting("DomainName"), host).ToUpper();
        }
        else
        {
            workstationaccount = String.Format("{0}\\{1}
quot;, 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
}

Now using the account we grabbed in the first function/process, we now try to verify and decide if we should show a login or auto-login the user.

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

I wrote a blog post about this a long time ago, here is a link to the wrapper for netapi32.dll and my Impersonation helper that I provided in the post Source Code Download

拥抱没勇气 2024-10-06 14:47:06

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

You cannot have both NTLM and FormsAuthentication in the same ASP.NET application. You will need two different applications in separate virtual directories.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文