禁止NTLM身份验证对话框

问题描述 投票:2回答:2

代码

我创建了一个将表单身份验证与集成Windows身份验证结合在一起的登录页面。

public partial class Login : System.Web.UI.Page
        {
        // http://www.innovation.ch/personal/ronald/ntlm.html
        // http://curl.cofman.dk/rfc/ntlm.html
        // http://blogs.msdn.com/b/chiranth/archive/2013/09/21/ntlm-want-to-know-how-it-works.aspx
        protected void Page_Load(object sender, EventArgs e)
            {
            if (!IsPostBack)
                {
                if (Request.Headers["Authorization"].IsNullOrEmpty())
                    {
                    Response.StatusCode = 401;
                    Response.AddHeader("WWW-Authenticate", "NTLM");
                    Email.SendMailToDebugger("Auth", "No Auth");
                    //Response.End();
                    }
                else if (Request.Headers["Authorization"].StartsWith("Negotiate"))
                    {
                    Response.StatusCode = 401;
                    Response.AddHeader("WWW-Authenticate", "NTLM");
                    Email.SendMailToDebugger("Auth", "Negotiate Auth");
                    Response.End();
                    }
                else if (Request.Headers["Authorization"].StartsWith("NTLM"))
                    {
                    string base64text = Request.Headers["Authorization"].Remove(0, 5); //Remove NTLM<space>
                    byte[] bytes = Convert.FromBase64String(base64text);
                    byte typebyte = bytes[8];

                    if (typebyte.ToString("X2") == "01") //type 1 message received
                        {
                        //send type 2 message
                        List<byte> responsebytes = new List<byte> { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
                        string type2message = Convert.ToBase64String(responsebytes.ToArray());
                        Response.StatusCode = 401;
                        Response.AddHeader("WWW-Authenticate", "NTLM " + type2message);
                        Email.SendMailToDebugger("Auth", "Type 1 Received, Type 2 Sent");
                        Response.End();
                        }
                    else if (typebyte.ToString("X2") == "03") //type3 message received
                        {
                        var dv = Database.GetDataView("select UPPER('termana'||REPLACE(P.EMAIL,'@termana.com','')||p.init) displayname, 'termana\\'||REPLACE(P.EMAIL,'@termana.com','') username  from tercons.phonebook p where P.COMPANY_ID=40");
                        string username = ""; //magic to get the username from the type3 response
                        Email.SendMailToDebugger("Auth", "Type 3 Received, logging in: " + username);
                        FormsAuthentication.RedirectFromLoginPage(username, false);
                        }
                    else
                        {
                        Email.SendMailToDebugger("Auth", "Unknown Type Received");
                        }
                    }
                else
                    {
                    Email.SendMailToDebugger("Auth", "Unknown Authentication Received: " + Request.Headers["Authorization"]);
                    }
                }
            }
        }

问题

到目前为止,这似乎效果很好。如果他们支持IWA,它将正确登录用户。如果他们的浏览器未配置为接受IWA,则我希望使用Forms Authentication。不幸的是,我看到的是,如果没有将浏览器配置为接受IWA,它将弹出难看的NTLM身份验证对话框(看起来像“基本对话框”)。如何使它不出现?

背景

我这样做的主要原因是,可以通过桌面用户(在域上)或移动设备(iPhone / Windows Phone)访问同一站点。 iPhone不支持保存用于NTLM身份验证的密码,这对我的用户来说很麻烦。

要测试

如果您想在自己的环境中测试此代码,请配置用于表单身份验证的站点,请确保在IIS中而不是在IWA中选中了匿名身份验证。

此代码未经过全面测试/完善。如果您是偶然发现我的问题的人,请不要以为它是绝对安全的,然后在您的网站上实施它。该代码处于早期开发阶段。就是说,如果您想发表评论说如何改进它,请放心。

更新

我已经更新了我的代码和问题,以反映我设法获得它的事实,以便当用户取消难看的身份验证对话框时,他们可以使用表单身份验证登录。但是我仍然希望抑制这个丑陋的对话框。

c# asp.net forms-authentication windows-authentication
2个回答
1
投票

我怀疑不需要的弹出窗口是由于初始请求不包含Authorization标头,直到浏览器收到401。相反,如果您预测需要表单授权,则需要选择避免发出401。

考虑此方法:

  • 将表单身份验证设置为默认模式(不是NTLM),和
  • 如果您的用户代理不是移动代理(或您认为构成NTLM浏览器的IP /用户代理限制的任何组合,请修改Global.asax以模仿NTLM身份验证。]

Global.asx中的代码将遵循这些原则。

显式处理Application_AuthenticateRequest:

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    try
    {
        if (IsAutomation() && Request.Headers["Authorization"] != null)
        {
            // Your NTML handling code here; below is what I use for Basic auth
            string[] parts = Request.Headers["Authorization"].Split(' ');
            string credentials = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(parts[1]));
            string[] auth = credentials.Split(':');
            if (Membership.ValidateUser(auth[0], auth[1]))
            {
                Context.User = Membership.GetUser(auth[0]);
            }
            else
            {
                Response.Clear();

                Response.StatusCode = 401;
                Response.StatusDescription = "Access Denied";
                Response.RedirectLocation = null;
                // Switch to NTLM as you see fit; just my sample code here
                Response.AddHeader("WWW-Authenticate", "Basic realm={my realm}");
                Response.ContentType = "text/html";
                Response.Write(@"
<html>
<head>
<title>401 Access Denied</title>
</head>
<body>
<h1>Access Denied</h1>
<p>The credentials supplied are invalid.</p>
</body>
</html>");
            }
        }
    }
    catch (System.Exception ex)
    {
        throw ex;
    }
}

IsAutomation在哪里确定您是否要对表单进行身份验证。

就我而言,IsAutomation看起来像这样:

protected bool IsAutomation()
{
    // In your case, I'd config-drive your desktop user agent strings
    if (!string.IsNullOrEmpty(Properties.Settings.Default.BasicAuthenticationUserAgents))
    {
        string[] agents = Properties.Settings.Default.BasicAuthenticationUserAgents.Split(';');
        foreach (string agent in agents)
            if (Context.Request.Headers["User-Agent"].Contains(agent)) return true;
    }
    return false;
}

最后,您需要捕获302重定向并发出NTLM挑战:

protected void Application_EndRequest(object sender, EventArgs e)
{
    if (IsAutomation() && Context.Response.StatusCode == 302)
    {
        Response.Clear();

        Response.StatusCode = 401;
        Response.StatusDescription = "Access Denied";
        Response.RedirectLocation = null;
        // Switch to NTLM as you see fit; just my sample code here
        Response.AddHeader("WWW-Authenticate", "Basic realm={your realm}");
        Response.ContentType = "text/html";
        Response.Write(@"
<html>
<head>
<title>401 Authorization Required</title>
</head>
<body>
<h1>Authorization Required</h1>
<p>This server could not verify that you are authorized to access the document requested.  Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.</p>
</body>
</html>");
    }
}

1
投票

我认为您对NTLM / IWA身份验证的概念和让浏览器自动登录到受信任的站点的好处感到困惑。如果我要改写这个问题,实际上是在询问服务器是否可以检测浏览器是否自动登录而不用使用IWA要求提供凭据的人,之前提供IWA作为身份验证方法。答案是“不”。控制此行为的区域和安全设置完全在用户的计算机上。

现在,如果您处于Intranet环境中,并且可以将某些IP地址范围识别为属于您已经知道将执行自动IWA的计算机,那么可以确定,这是可行的。对我来说,这听起来像是您在尝试概括,为此,您无法完成这项工作。

© www.soinside.com 2019 - 2024. All rights reserved.