65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (21投票s)

2009 年 6 月 23 日

CPL

3分钟阅读

viewsIcon

376469

downloadIcon

4841

关于“如何在 ASP.NET 中使用表单身份验证对 Windows 用户进行身份验证?”的文章。

Windows Authentication using Form Authentication

背景

上个月,我完成了一个小型任务,即使用表单身份验证来验证 Windows 帐户(域或本地)。此任务的目的是使用户能够使用任何有效的 Windows 帐户登录(而不是自动身份验证已登录的 Windows 用户)。

这是一个有趣的挑战,我决定与您分享我的经验。

要求

应用程序应通过表单身份验证来验证 Windows 用户,这样当前登录的用户就不必仅凭其 Windows 帐户登录到应用程序。他应该能够使用任何有效的 Windows 帐户登录。

解决方案

我们需要执行以下步骤来实现所需的功能:

  1. web.config 中配置授权和身份验证设置
  2. 一个登录页面,并执行逻辑来验证提供的 Windows 用户凭据
  3. 如果在步骤 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。谢谢!

© . All rights reserved.