禁止NTLM身份validation对话框

我创建了一个将表单身份validation与集成Windows身份validation相结合的登录页面。

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 byte[] bytes = Convert.FromBase64String(base64text); byte typebyte = bytes[8]; if (typebyte.ToString("X2") == "01") //type 1 message received { //send type 2 message List responsebytes = new List { 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,我想回到表单身份validation。 不幸的是,我看到的情况是,如果浏览器没有配置为接受IWA,它会弹出丑陋的NTLM身份validation对话框(看起来像基本对话框)。 如何让它不出现?

背景

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

去测试

如果要在自己的环境中测试此代码,请配置站点以进行表单身份validation,确保在IIS中检查匿名身份validation,而不是IWA。

此代码未经过充分测试/充实。 如果你是一个偶然的人偶然发现我的问题,不要认为它是完全安全的,然后在你的网站上实现它。 此代码处于早期开发阶段。 也就是说,如果你想留下评论说如何改进它,请随意。

更新

我已经更新了我的代码和问题,以反映我设法得到它的事实,以便当用户取消丑陋的身份validation对话框时,他们能够使用表单身份validation登录。 但我仍然希望这个丑陋的对话被压制。

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

考虑这种方法:

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

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(@"   401 Access Denied   

Access Denied

The credentials supplied are invalid.

"); } } } catch (System.Exception ex) { throw ex; } }

IsAutomation在何处确定您是否需要表单身份validation。

就我而言,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(@"   401 Authorization Required   

Authorization Required

This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (eg, bad password), or your browser doesn't understand how to supply the credentials required.

"); } }

我认为你在NTLM / IWA身份validation的概念和让浏览器自动登录你的可信站点的细节之间感到困惑。 如果我要重新解释这个问题,那么在提供IWA作为身份validation方法之前 ,您实际上是在询问服务器是否可以检测浏览器是否会在没有使用IWA请求凭据的情况下自动登录某人。 对此的答案是响亮的“不”。 控制此行为的区域和安全设置完全在用户的计算机上。

现在,如果您处于Intranet环境中,并且您可以将某些IP地址范围识别为属于您已知的将执行自动IWA的计算机,那么确定,这是有效的。 这听起来像是你想要概括,为此,你无法做到这一点。