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

文章翻译 文章翻译
文章编号: 326340 - 查看本文应用于的产品
展开全部 | 关闭全部

本文内容

概要

本文分步介绍了 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 System
    Imports System.Text
    Imports System.Collections
    Imports System.DirectoryServices
    
    Namespace 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 Class
    End 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.Security
    Imports 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 try
    end 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 知识库中相应的文章:
306590 INFO:ASP.NET 安全性概述
317012 ASP.NET 中的进程和请求标识
306238 如何使用 Visual Basic .NET 在 ASP.NET 应用程序中通过基于窗体的身份验证实现基于角色的安全性
313091 如何使用 Visual Basic .NET 创建在窗体身份验证中使用的密钥
313116 PRB:窗体身份验证请求未定向到 loginUrl 页
返回页首

属性

文章编号: 326340 - 最后修改: 2006年9月1日 - 修订: 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
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。

提供反馈

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com