你目前正处于脱机状态,正在等待 Internet 重新连接

如何使用窗体身份验证和 Visual Basic .NET 进行 Active Directory 身份验证

概要
本文分步介绍了 ASP.NET 应用程序如何使用窗体身份验证,以允许用户使用轻型目录访问协议 (LDAP) 进行 Active Directory 身份验证。

对用户进行身份验证及重定向之后,可以使用 Global.asax 文件的 Application_AuthenticateRequest 方法将一个 GenericPrincipal 对象存储在贯穿整个请求的 HttpContext.User 属性中。

返回页首

在 Visual Basic .NET 中创建一个 ASP.NET Web 应用程序

按照以下步骤在 Visual Basic .NET 中创建一个名为 FormsAuthAd 的新 ASP.NET Web 应用程序:
  1. 启动 Microsoft Visual Studio .NET。
  2. 文件菜单上,指向新建,然后单击项目
  3. 单击项目类型下的 Visual Basic 项目,然后单击模板下的 ASP.NET Web 应用程序
  4. 位置框中,键入 http://<servername>/FormsAuthAd(如果使用本地服务器,则替换为 http://localhost,以便得到 http://localhost/FormsAuthAd),然后单击确定
  5. 在解决方案资源管理器中,右键单击引用节点,然后单击添加引用
  6. 添加引用对话框中的 .NET 选项卡上,依次单击 System.DirectoryServices.dll选择,然后单击确定
返回页首

编写身份验证代码

按照以下步骤创建一个名为 LdapAuthentication.vb 的新类文件:
  1. 在解决方案资源管理器中,右键单击该项目节点,指向添加,然后单击添加新项
  2. 单击模板下的
  3. 名称框中键入 LdapAuthentication.vb,然后单击打开
  4. 将 LdapAuthentication.vb 文件中的现有代码替换为以下代码:
    Imports SystemImports System.TextImports System.CollectionsImports System.DirectoryServicesNamespace FormsAuth    Public Class LdapAuthentication        Dim _path As String        Dim _filterAttribute As String        Public Sub New(ByVal path As String)            _path = path        End Sub        Public Function IsAuthenticated(ByVal domain As String, ByVal username As String, ByVal pwd As String) As Boolean            Dim domainAndUsername As String = domain & "\" & username            Dim entry As DirectoryEntry = New DirectoryEntry(_path, domainAndUsername, pwd)            Try                'Bind to the native AdsObject to force authentication.			                Dim obj As Object = entry.NativeObject                Dim search As DirectorySearcher = New DirectorySearcher(entry)                search.Filter = "(SAMAccountName=" & username & ")"                search.PropertiesToLoad.Add("cn")                Dim result As SearchResult = search.FindOne()                If (result Is Nothing) Then                    Return False                End If                'Update the new path to the user in the directory.                _path = result.Path                _filterAttribute = CType(result.Properties("cn")(0), String)            Catch ex As Exception                Throw New Exception("Error authenticating user. " & ex.Message)            End Try            Return True        End Function        Public Function GetGroups() As String            Dim search As DirectorySearcher = New DirectorySearcher(_path)            search.Filter = "(cn=" & _filterAttribute & ")"            search.PropertiesToLoad.Add("memberOf")            Dim groupNames As StringBuilder = New StringBuilder()            Try                Dim result As SearchResult = search.FindOne()                Dim propertyCount As Integer = result.Properties("memberOf").Count                Dim dn As String                Dim equalsIndex, commaIndex                Dim propertyCounter As Integer                For propertyCounter = 0 To propertyCount - 1                    dn = CType(result.Properties("memberOf")(propertyCounter), String)                    equalsIndex = dn.IndexOf("=", 1)                    commaIndex = dn.IndexOf(",", 1)                    If (equalsIndex = -1) Then                        Return Nothing                    End If                    groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1))                    groupNames.Append("|")                Next            Catch ex As Exception                Throw New Exception("Error obtaining group names. " & ex.Message)            End Try            Return groupNames.ToString()        End Function    End ClassEnd Namespace					

代码说明

身份验证代码接受域、用户名、密码和 Active Directory 中树的路径。该代码使用 LDAP 目录提供程序。
用户身份验证
Logon.aspx 页中的代码调用 LdapAuthentication.IsAuthenticated 方法,并传递到从用户那里收集到的凭据中。然后,创建一个 DirectoryEntry 对象,并带有目录树的路径、用户名和密码。用户名必须是“domain\username”格式。然后,DirectoryEntry 对象通过获取 NativeObject 属性,尝试强制进行 AdsObject 绑定。如果成功,将通过创建一个 DirectorySearcher 对象并对 SAMAccountName 进行筛选,获取用户的 CN 属性。用户身份得到验证之后,IsAuthenticated 方法将返回 true

注意:如果使用 LDAP 来绑定到与 Active Directory 相关的对象,则将使用 TCP 端口。如果通过 System.DirectoryServices 命名空间增强对 LDAP 的使用,则可能会使用所有可用的 TCP 端口。通过重新使用用于验证用户身份的连接,能够减少 TCP 负载。
用户组
为获取用户所属的组列表,该代码将调用 LdapAuthentication.GetGroups 方法。LdapAuthentication.GetGroups 方法通过创建一个 DirectorySearcher 对象并根据 memberOf 属性进行筛选,获取用户所属的安全和通讯组列表。此方法返回的组列表以竖线 (|) 分隔。

请注意,LdapAuthentication.GetGroups 方法可操纵和截断字符串。这样将减少在身份验证 cookie 中存储的字符串长度。如果字符串未被截断,每个组的格式将按如下显示:
CN=...,...,DC=domain,DC=com				
这可创建一个很长的字符串。如果该字符串的长度大于 cookie 的长度,则不会创建身份验证 cookie。如果该字符串的长度可能会大于 cookie 的长度,则可能希望将组信息存储在 ASP.NET 缓存对象或一个数据库中。另外,您还可能希望加密组信息并将该信息存储在一个隐藏的窗体域中。

返回页首

编写 Global.asax 代码

Global.asax 文件中的代码提供了一个 Application_AuthenticateRequest 事件处理程序。此事件处理程序从 Context.Request.Cookies 集合中检索身份验证 cookie,解密该 cookie,并检索将以 FormsAuthenticationTicket.UserData 属性存储的组列表。在 Logon.aspx 页中创建的这些组将以竖线分隔的列表形式显示。

该代码分析字符串数组中的字符串,以创建一个 GenericPrincipal 对象。创建了 GenericPrincipal 对象后,该对象将被放置在 HttpContext.User属性中。
  1. 在解决方案资源管理器中,右键单击 Global.asax,然后单击查看代码
  2. 在 Global.asax.vb 文件后面的代码顶部添加以下代码:
    Imports System.Web.SecurityImports System.Security.Principal					
  3. Application_AuthenticateRequest 的现有空事件处理程序替换为以下代码:
    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)        ' Fires upon attempting to authenticate the use        Dim cookieName As String = FormsAuthentication.FormsCookieName        Dim authCookie As HttpCookie = Context.Request.Cookies(cookieName)        If (authCookie Is Nothing) Then            'There is no authentication cookie.            Return        End If        Dim authTicket As FormsAuthenticationTicket = Nothing        Try            authTicket = FormsAuthentication.Decrypt(authCookie.Value)        Catch ex As Exception            'Write the exception to the Event Log.            Return        End Try        If (authTicket Is Nothing) Then            'Cookie failed to decrypt.            Return        End If        'When the ticket was created, the UserData property was assigned a        'pipe-delimited string of group names.        Dim groups As String() = authTicket.UserData.Split(New Char() {"|"})        'Create an Identity.        Dim id As GenericIdentity = New GenericIdentity(authTicket.Name, "LdapAuthentication")        'This principal flows throughout the request.        Dim principal As GenericPrincipal = New GenericPrincipal(id, groups)        Context.User = principal    End Sub					
返回页首

修改 Web.config 文件

在本节中,将在 Web.config 文件中配置 formsauthenticationauthorization 元素。作出这些更改之后,只有经过身份验证的用户才能访问该应用程序,未经身份验证的请求将被重定向到 Logon.aspx 页。可以将此配置修改为只允许特定用户和组访问该应用程序。

将 Web.config 文件中的现有代码替换为以下代码:
<?xml version="1.0" encoding="utf-8" ?><configuration>      <system.web>    <authentication mode="Forms">      <forms loginUrl="logon.aspx" name="adAuthCookie" timeout="60" path="/" >      </forms>    </authentication>	    <authorization>	      <deny users="?" />      <allow users="*" />    </authorization>	    <identity impersonate="true" />  </system.web></configuration>				
请注意 identity impersonate="true" / 配置元素。它将导致 ASP.NET 模拟在 Microsoft Internet 信息服务 (IIS) 中被配置为匿名帐户的帐户。该配置的结果是,对此应用程序的所有请求都将在已配置帐户的安全上下文中运行。用户提供进行 Active Directory 身份验证的凭据,但是访问 Active Directory 的帐户是已配置帐户。有关更多信息,请参阅参考 一节。

返回页首

为匿名身份验证配置 IIS

要为匿名身份验证配置 IIS,请执行以下步骤:
  1. 在 Internet 信息服务 (IIS) 管理控制台中,右键单击 "FormsAuthAd" 的“虚拟目录”节点。
  2. 单击属性,然后单击目录安全性选项卡。
  3. 单击匿名访问和身份验证控制下的编辑
  4. 选中匿名访问复选框。
  5. 使该应用程序的匿名帐户成为对 Active Directory 具有权限的帐户。
  6. 单击以清除“允许 IIS 控制密码”复选框。
默认的 IUSR_computername 帐户对 Active Directory 不具有权限。

返回页首

创建 Logon.aspx 页

按照以下步骤创建一个名为 Logon.aspx 的新 ASP.NET Web 窗体:
  1. 在解决方案资源管理器中,右键单击该项目节点,指向添加,然后单击添加 Web 窗体
  2. 名称框中键入 Logon.aspx,然后单击打开
  3. 在解决方案资源管理器中,右键单击 Logon.aspx,然后单击查看设计器
  4. 单击“设计器”中的 HTML 选项卡。
  5. 将现有代码替换为以下代码:
    <%@ Page language="vb" AutoEventWireup="true" %><%@ Import Namespace="FormsAuthAd.FormsAuth" %><html>	<body>		<form id="Login" method="post" runat="server">			<asp:Label ID="Label1" Runat="server">Domain:</asp:Label>			<asp:TextBox ID="txtDomain" Runat="server"></asp:TextBox><br>			<asp:Label ID="Label2" Runat="server">Username:</asp:Label>			<asp:TextBox ID="txtUsername" Runat="server"></asp:TextBox><br>			<asp:Label ID="Label3" Runat="server">Password:</asp:Label>			<asp:TextBox ID="txtPassword" Runat="server" TextMode="Password"></asp:TextBox><br>			<asp:Button ID="btnLogin" Runat="server" Text="Login" OnClick="Login_Click"></asp:Button><br>			<asp:Label ID="errorLabel" Runat="server" ForeColor="#ff3300"></asp:Label><br>			<asp:CheckBox ID="chkPersist" Runat="server" Text="Persist Cookie" />		</form>	</body></html><script runat="server">sub Login_Click(sender as object,e as EventArgs)  Dim adPath as String = "LDAP://DC=..,DC=.." 'Path to your LDAP directory server  Dim adAuth as LdapAuthentication = new LdapAuthentication(adPath)  try    if(true = adAuth.IsAuthenticated(txtDomain.Text, txtUsername.Text, txtPassword.Text)) then      Dim groups as string = adAuth.GetGroups()      'Create the ticket, and add the groups.      Dim isCookiePersistent as boolean = chkPersist.Checked      Dim authTicket as FormsAuthenticationTicket = new FormsAuthenticationTicket(1, _           txtUsername.Text,DateTime.Now, DateTime.Now.AddMinutes(60), isCookiePersistent, groups)	      'Encrypt the ticket.      Dim encryptedTicket as String = FormsAuthentication.Encrypt(authTicket)		      'Create a cookie, and then add the encrypted ticket to the cookie as data.      Dim authCookie as HttpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)      if(isCookiePersistent = true) then		authCookie.Expires = authTicket.Expiration      end if				      'Add the cookie to the outgoing cookies collection.      Response.Cookies.Add(authCookie)	      'You can redirect now.      Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUsername.Text, false))        else      errorLabel.Text = "Authentication did not succeed. Check user name and password."    end if   catch ex as Exception    errorLabel.Text = "Error authenticating. " & ex.Message  end tryend sub</script>					
  6. 将 Logon.aspx 页中的路径修改为指向您的 LDAP Directory 服务器。
Logon.aspx 页是一个收集用户信息并调用 LdapAuthentication 类中的方法的页面。该代码对用户进行身份验证并获取了组列表之后,将按顺序执行以下操作:
  • 创建一个 FormsAuthenticationTicket 对象;
  • 加密票证;
  • 将已加密的票证添加到 cookie 中;
  • 将该 cookie 添加到 HttpResponse.Cookies 集合中;
  • 将请求重定向到原来请求的 URL。
返回页首

修改 WebForm1.aspx 页

WebForm1.aspx 页是原来请求的页面。当用户请求该页时,该请求将被重定向到 Logon.aspx 页。对该请求进行身份验证后,它将被重定向到 WebForm1.aspx 页。
  1. 在解决方案资源管理器中,右键单击 WebForm1.aspx,然后单击查看设计器
  2. 单击“设计器”中的 HTML 选项卡。
  3. 将现有代码替换为以下代码:
    <%@ Page language="vb" AutoEventWireup="true" %><%@ Import Namespace="System.Security.Principal" %><html>	<body>		<form id="Form1" method="post" runat="server">			<asp:Label ID="lblName" Runat="server" /><br>			<asp:Label ID="lblAuthType" Runat="server" />		</form>	</body></html><script runat="server">sub Page_Load(sender as object, e as EventArgs)  lblName.Text = "Hello " + Context.User.Identity.Name & "."  lblAuthType.Text = "You were authenticated using " &   Context.User.Identity.AuthenticationType & "."end sub</script>					
  4. 保存所有文件,然后编译该项目。
  5. 请求 WebForm1.aspx 页。请注意,您将被重定向到 Logon.aspx。
  6. 键入登录凭据,然后单击提交。当重定向到 WebForm1.aspx 时,请注意会显示您的用户名,而且 LdapAuthenticationContext.User.Identity.AuthenticationType 属性的身份验证类型。
注意:Microsoft 建议您在使用窗体身份验证时,使用安全套接字层 (SSL) 加密。这样做的原因是:用户是基于身份验证 cookie 进行标识的,而在此应用程序上的 SSL 加密可防止任何人威胁到身份验证 cookie 和正在传输的任何其他重要信息的安全。

返回页首
参考
有关更多信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
306590INFO:ASP.NET 安全性概述
317012 ASP.NET 中的进程和请求标识
306238 如何使用 Visual Basic .NET 在 ASP.NET 应用程序中通过基于窗体的身份验证实现基于角色的安全性
313091 如何使用 Visual Basic .NET 创建在窗体身份验证中使用的密钥
313116 PRB:窗体身份验证请求未定向到 loginUrl 页
返回页首
属性

文章 ID:326340 - 上次审阅时间:09/01/2006 02:53:26 - 修订版本: 4.3

  • Microsoft ASP.NET 1.0
  • Microsoft Visual .NET 2002 标准版
  • Microsoft ASP.NET 1.1
  • Microsoft Visual Basic .NET 2003 标准版
  • kbconfig kbcookie kbhowtomaster kbsecurity kbwebforms KB326340
反馈