我创建了一个将表单身份验证与集成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,我想回到表单身份验证。不幸的是,我看到的情况是,如果浏览器没有配置为接受IWA,它会弹出丑陋的NTLM身份验证对话框(看起来像基本对话框)。如何让它不出现?
我这样做的主要原因是可以通过桌面用户(在域上)或移动设备(iPhone / Windows Phone)访问同一站点。 iPhone并不支持为NTLM身份验证保存密码,这对我的用户来说很麻烦。
如果要在自己的环境中测试此代码,请为表单身份验证配置站点,请确保在IIS中检查匿名身份验证,而不是IWA。
此代码未经过充分测试/充实。如果您是一个偶然的人,我会在我的问题上遇到困难,请不要认为它非常安全,然后在您的网站上实施。此代码处于早期开发阶段。也就是说,如果你想留下评论说如何改进它,请随意。
我已经更新了我的代码和问题,以反映我设法获得它的事实,以便当用户取消丑陋的身份验证对话框时,他们能够使用表单身份验证登录。但我仍然希望这个丑陋的对话被压制。
答案 0 :(得分:1)
我认为您对NTLM / IWA身份验证的概念感到困惑,以及让浏览器自动为您注册受信任的站点的细节。如果我要重新解释这个问题,您实际上是在询问服务器是否可以检测浏览器是否会在没有使用IWA请求凭据的情况下自动登录某人,之前您提供IWA作为方法认证。对此的答案是响亮的&#34;没有。&#34;控制此行为的区域和安全设置完全在用户的计算机上。
现在,如果您在Intranet环境中,并且您可以将某些IP地址范围识别为属于您已知的机器,那么它将执行自动IWA,那么确定,这是有效的。这听起来像是你在尝试概括,为此,你无法做到这一点。
答案 1 :(得分:1)
我怀疑不需要的弹出窗口来自初始请求,不包含Authorization
标头,直到浏览器收到401。相反,如果您预测需要表单授权,则需要选择避免发出401。
考虑这种方法:
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>");
}
}