使用表单身份验证进行 Windows 身份验证





5.00/5 (21投票s)
关于“如何在 ASP.NET 中使用表单身份验证对 Windows 用户进行身份验证?”的文章。

背景
上个月,我完成了一个小型任务,即使用表单身份验证来验证 Windows 帐户(域或本地)。此任务的目的是使用户能够使用任何有效的 Windows 帐户登录(而不是自动身份验证已登录的 Windows 用户)。
这是一个有趣的挑战,我决定与您分享我的经验。
要求
应用程序应通过表单身份验证来验证 Windows 用户,这样当前登录的用户就不必仅凭其 Windows 帐户登录到应用程序。他应该能够使用任何有效的 Windows 帐户登录。
解决方案
我们需要执行以下步骤来实现所需的功能:
- 在 web.config 中配置授权和身份验证设置
- 一个登录页面,并执行逻辑来验证提供的 Windows 用户凭据
- 如果在步骤 2 中验证了提供的凭据,则生成一个身份验证令牌,以便用户能够导航到您应用程序的授权页面。
1. 在 web.config 中配置授权和身份验证设置
我们需要使用表单身份验证。用户将在表单中输入其 Windows 凭据,我们将在步骤 2 中使用自定义逻辑来验证提供的 Windows 凭据。
<authentication mode="Forms">
<forms loginUrl="login.aspx" name=".ASPXFORMSAUTH"></forms>
</authentication>
要限制匿名访问,您需要在 web.config 中进行以下授权设置:
<authorization>
<deny users="?"/>
</authorization>
2. 创建一个登录页面并执行逻辑来验证提供的 Windows 用户凭据
我们需要创建一个登录页面(例如 login.aspx)来从用户那里获取用户名和密码信息,然后进行验证。我们可以选择不同的选项来验证 Windows 凭据,我选择的方法是使用一个名为 Advapi32.dll 的 Win32 API 的 LogonUser()
方法。LogonUser
函数尝试登录到本地计算机。此方法将用户名、密码和其他信息作为输入,并返回一个布尔值以指示用户是否已登录。如果返回 true
,则表示提供的用户名和密码是正确的。要在我们的类中使用此方法,我们需要包含以下命名空间:
using System.Runtime.InteropServices;
并添加带有 DLLImport
属性的方法声明(因为这是 Win32 DLL 的方法,而 Win32 DLL 是非托管 DLL)。
[DllImport("ADVAPI32.dll", EntryPoint =
"LogonUserW", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain,
string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
根据 MSDN 文档:
lpszUsername [in]
指向一个 null
终止的 string
的指针,指定用户名称。这是要登录的用户名。如果使用用户主体名称 (UPN) 格式 User@DNSDomainName,则 lpszDomain
参数必须为 NULL
。
lpszDomain [in, optional]
指向一个 null
终止的 string
的指针,指定帐户数据库包含 lpszUsername
帐户的域或服务器的名称。如果此参数为 NULL
,则必须以 UPN 格式指定用户名。如果此参数为“.
”,则该函数仅使用本地帐户数据库来验证帐户。
lpszPassword [in]
指向一个 null
终止的 string
的指针,指定由 lpszUsername
指定的用户帐户的明文密码。完成使用密码后,通过调用 SecureZeroMemory
函数将密码从内存中清除。有关保护密码的更多信息,请参阅处理密码。
dwLogonType [in]
要执行的登录操作的类型。
dwLogonProvider [in]
指定登录提供程序。
phToken [out]
指向一个句柄变量的指针,该变量接收表示指定用户的令牌的句柄。
3. 如果在步骤 2 中验证了提供的凭据,则生成身份验证令牌
如果 LogonUser()
方法验证了提供的凭据,那么我们需要生成一个身份验证令牌,以便用户能够导航到应用程序的授权页面。
FormsAuthentication.RedirectFromLoginPage()
或者
FormsAuthentication.SetAuthCookie()
可用于此目的。
这是用于身份验证和生成身份验证令牌的登录按钮的 Click 处理程序代码。注释将帮助您理解代码。
protected void btnLogin_Click(object sender, EventArgs e)
{
string domainName = GetDomainName(txtUserName.Text); // Extract domain name
// form provided DomainUsername e.g Domainname\Username
string userName = GetUsername(txtUserName.Text); // Extract user name
// from provided DomainUsername e.g Domainname\Username
IntPtr token = IntPtr.Zero;
//userName, domainName and Password parameters are very obvious.
//dwLogonType (3rd parameter):
// I used LOGON32_LOGON_INTERACTIVE, This logon type
// is intended for users who will be interactively using the computer,
// such as a user being logged on by a terminal server, remote shell,
// or similar process.
// This logon type has the additional expense of caching
// logon information for disconnected operations.
// For more details about this parameter please
// see http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
//dwLogonProvider (4th parameter) :
// I used LOGON32_PROVIDER_DEFAUL, This provider use the standard
// logon provider for the system.
// The default security provider is negotiate, unless you pass
// NULL for the domain name and the user name is not in UPN format.
// In this case, the default provider is NTLM. For more details
// about this parameter please see
// http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
//phToken (5th parameter):
// A pointer to a handle variable that receives a handle to
// a token that represents the specified user.
// We can use this handler for impersonation purpose.
bool result = LogonUser(userName, domainName,
txtPassword.Text, 2, 0, ref token);
if (result)
{
//If Successfully authenticated
//When an unauthenticated user try to visit any page of your
//application that is only allowed to view by authenticated users
//then ASP.NET automatically redirect that user to login form
//and add ReturnUrl query string parameter that contain the URL of
//a page that user want to visit, So that we can redirect the
//user to that page after authenticated.
//FormsAuthentication.RedirectFromLoginPage() method not only
//redirect the user to that page but also generate an authentication
//token for that user.
if (string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
{
FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
}
//If ReturnUrl query string parameter is not present,
//then we need to generate authentication token and redirect the user
//to any page ( according to your application need).
//FormsAuthentication.SetAuthCookie() method will
//generate Authentication token
else
{
FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
Response.Redirect("default.aspx");
}
}
else
{
//If not authenticated then display an error message
Response.Write("Invalid username or password.");
}
}
让我们把它们整合在一起
Login.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs"
Inherits="Login" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Windows Authentication Using Form Authentication</title>
<style type="text/css">
.style1
{
width: 100%;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<table class="style1">
<tr>
<td>
<asp:Label ID="lblUserName" runat="server" Text="User Name:">
</asp:Label>
</td>
<td>
<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lblPassword" runat="server" Text="Password:">
</asp:Label>
</td>
<td>
<asp:TextBox ID="txtPassword" runat="server" TextMode="Password" >
</asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Button ID="btnLogin" runat="server" onclick="btnLogin_Click"
Text="Login" />
</td>
</tr>
</table>
</div>
<p>
</p>
</form>
</body>
</html>
Login.aspx.cs
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Runtime.InteropServices;
public partial class Login : System.Web.UI.Page
{
[DllImport("ADVAPI32.dll", EntryPoint =
"LogonUserW", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool LogonUser(string lpszUsername,
string lpszDomain, string lpszPassword, int dwLogonType,
int dwLogonProvider, ref IntPtr phToken);
/// <summary>
/// Parses the string to pull the domain name out.
/// </summary>
/// <param name="usernameDomain">The string to parse that must
/// contain the domain in either the domain\username or UPN format
/// username@domain</param>
/// <returns>The domain name or "" if not domain is found.</returns>
public static string GetDomainName(string usernameDomain)
{
if (string.IsNullOrEmpty(usernameDomain))
{
throw (new ArgumentException("Argument can't be null.", "usernameDomain"));
}
if (usernameDomain.Contains("\\"))
{
int index = usernameDomain.IndexOf("\\");
return usernameDomain.Substring(0, index);
}
else if (usernameDomain.Contains("@"))
{
int index = usernameDomain.IndexOf("@");
return usernameDomain.Substring(index + 1);
}
else
{
return "";
}
}
/// <summary>
/// Parses the string to pull the user name out.
/// </summary>
/// <param name="usernameDomain">The string to parse that must
/// contain the username in either the domain\username or UPN format
/// username@domain</param>
/// <returns>The username or the string if no domain is found.</returns>
public static string GetUsername(string usernameDomain)
{
if (string.IsNullOrEmpty(usernameDomain))
{
throw (new ArgumentException("Argument can't be null.", "usernameDomain"));
}
if (usernameDomain.Contains("\\"))
{
int index = usernameDomain.IndexOf("\\");
return usernameDomain.Substring(index + 1);
}
else if (usernameDomain.Contains("@"))
{
int index = usernameDomain.IndexOf("@");
return usernameDomain.Substring(0, index);
}
else
{
return usernameDomain;
}
}
protected void btnLogin_Click(object sender, EventArgs e)
{
string domainName = GetDomainName(txtUserName.Text); // Extract domain name
//form provide DomainUsername e.g Domainname\Username
string userName = GetUsername(txtUserName.Text); // Extract user name
//from provided DomainUsername e.g Domainname\Username
IntPtr token = IntPtr.Zero;
//userName, domainName and Password parameters are very obvious.
//dwLogonType (3rd parameter):
// I used LOGON32_LOGON_INTERACTIVE, This logon type is
// intended for users who will be interactively using the computer,
// such as a user being logged on by a terminal server, remote shell,
// or similar process.
// This logon type has the additional expense of caching
// logon information for disconnected operations.
// For more details about this parameter please see
// http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
//dwLogonProvider (4th parameter) :
// I used LOGON32_PROVIDER_DEFAUL, This provider use the standard
// logon provider for the system.
// The default security provider is negotiate, unless you pass
// NULL for the domain name and the user name is not in UPN format.
// In this case, the default provider is NTLM. For more details
// about this parameter please see
// http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
//phToken (5th parameter):
// A pointer to a handle variable that receives a handle to a
// token that represents the specified user. We can use this handler
// for impersonation purpose.
bool result = LogonUser(userName, domainName, txtPassword.Text, 2, 0, ref token);
if (result)
{
//If Successfully authenticated
//When an unauthenticated user try to visit any page of your
//application that is only allowed to view by authenticated users then,
//ASP.NET automatically redirect the user to login form and add
//ReturnUrl query string parameter that contain the URL of a page that
//user want to visit, So that we can redirect the user to that page after
//authenticated. FormsAuthentication.RedirectFromLoginPage() method
//not only redirect the user to that page but also generate an
//authentication token for that user.
if (string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
{
FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
}
//If ReturnUrl query string parameter is not present,
//then we need to generate authentication token and redirect
//the user to any page ( according to your application need).
//FormsAuthentication.SetAuthCookie()
//method will generate Authentication token
else
{
FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
Response.Redirect("default.aspx");
}
}
else
{
//If not authenticated then display an error message
Response.Write("Invalid username or password.");
}
}
}
我们完成了!!!
我们已通过表单身份验证完成了 Windows 帐户的身份验证。
反馈
您的反馈对我非常有帮助。您可以通过电子邮件发送给我:akhhttar@gmail.com。谢谢!