CSDN博客

img ycl111

如何通过 Active Directory 使用 Forms 身份验证

发表于2004/12/29 9:47:00  2497人阅读

分类: .net 文章

发布日期: 10/28/2004 | 更新日期: 10/28/2004
a

浏览全部“安全性指南”主题

Microsoft Corporation

目标

本模块用于:

创建使用 Forms 身份验证来针对 Active Directory 对用户进行身份验证的 Web 应用程序。

从 Active Directory 中获取经过身份验证的用户所属的组列表和通讯组列表。

创建一个使用 HttpContext.Current.User 属性与用户的 Web 请求关联的 GenericPrincipal 对象。

适用于:

本模块适用于下列产品和技术:

Microsoft Windows_ XP 或 Windows 2000 Server(带 Service Pack 3)以及更高版本的操作系统

Active Directory

Microsoft .NET Framework 版本 1.0(带 Service Pack 2)以及更高版本

Microsoft Visual Studio_ 1.0 .NET 开发系统以及更高版本

Microsoft Visual C#_ .NET 开发工具

Microsoft SQL Server™ 2000(带 Service Pack 2)以及更高版本

本模块的使用方法

要最大程度利用本模块:

必须有使用 Visual C# .NET 和 Visual Studio .NET 的经验。

必须有使用 ASP.NET 开发 Web 应用程序的经验。

必须有使用 Active Directory 的经验。

需要访问一个可用于测试应用程序的 Active Directory 的实例 — 此实例不应是一个生产系统。

阅读模块“Authentication and Authorization”。本模块提供了有关各种身份验证机制的详细信息,并讨论了基于 .NET 角色的安全性。

阅读模块“ASP.NET Security”。本模块提供了有关 ASP.NET Web Forms 身份验证的详细信息。

*
本页内容
摘要 摘要
创建一个有登录页的 Web 应用程序 创建一个有登录页的 Web 应用程序
配置 Web 应用程序进行 Forms 身份验证 配置 Web 应用程序进行 Forms 身份验证
开发在 Active Directory 中查找用户的 LDAP 身份验证代码 开发在 Active Directory 中查找用户的 LDAP 身份验证代码
开发查找用户的组成员身份的 LDAP 组检索代码 开发查找用户的组成员身份的 LDAP 组检索代码
对用户进行身份验证并创建 Forms 身份验证票 对用户进行身份验证并创建 Forms 身份验证票
实现身份验证请求处理程序来构造 GenericPrincipal 对象 实现身份验证请求处理程序来构造 GenericPrincipal 对象
测试应用程序 测试应用程序

摘要

ASP.NET Forms 身份验证允许用户将凭据(用户名和密码)输入到一个 Web 窗体来标识自身。在收到这些凭据时,Web 应用程序可以根据数据源来检查这些凭据,从而对用户进行身份验证。

本模块描述如何通过使用轻量级目录访问协议 (LDAP) 根据 Microsoft Active Directory_ 目录服务来对用户进行身份身份。它还描述了如何检索用户所属的安全组列表和通讯组列表以及配置与基于 .NET 的角色授权一起使用的 GenericPrincipal 对象。

创建一个有登录页的 Web 应用程序

此过程创建一个简单的 Visual C# Web 应用程序,此程序包含一个登录页和一个默认页。登录页允许用户输入用户名和密码,而默认页显示与当前 Web 请求关联的标识名称和组成员身份信息。

要创建一个有登录页的 Web 应用程序,请执行下列步骤:

1.

启动 Visual Studio .NET 并创建一个名为 FormsAuthAD 的新 Visual C# ASP.NET Web 应用程序。

2.

使用解决方案资源管理器将 WebForm1.aspx 重命名为 Logon.aspx。

3.

为 System.DirectoryServices.dll 添加一个新的程序集引用。此操作提供了对 System.DirectoryServices 命名空间的访问,而此命名空间包含了托管类型,这有助于 Active Directory 进行查询和操作。

4.

将表 1 中列出的控件添加到 Logon.aspx 中来创建简单的登录窗体。

表 1: Logon.aspx 控件
控件类型 文本 ID

Label

Domain Name:

-

Label

User Name:

-

Label

Password

-

Text Box

-

txtDomainName

Text Box

-

txtUserName

Text Box

-

txtPassword

Button

Log On

btnLogon

Label

-

lblError

1.

将 txtPassword 的 TextMode 属性设置为 Password

2.

在解决方案资源管理器中,右键单击 FormsAuthAD,指向 Add,然后单击 Add Web Form

3.

Name 字段中,键入 default.aspx,然后单击 Open

4.

在解决方案资源管理器中,右键单击 default.aspx,然后单击 Set As Start Page

5.

双击 default.aspx 显示页加载事件处理程序。

6.

将下列代码添加到事件处理程序中以显示与当前 Web 请求关联的标识名称。

Response.Write( HttpContext.Current.User.Identity.Name );

配置 Web 应用程序进行 Forms 身份验证

此过程通过编辑应用程序的 Web.config 文件来配置应用程序以进行 Forms 身份验证。

要配置 Web 应用程序以进行 Forms 身份验证,请执行下列步骤:

1.

使用解决方案资源管理器打开 Web.config。

2.

定位到 <authentication> 元素并将 mode 属性更改为 Forms

3.

将下列 <forms> 元素作为身份验证元素的子元素进行添加,并设置 loginUrlNametimeoutpath 属性,如下列代码所示。

  <authentication mode="Forms">
  <forms loginUrl="logon.aspx" name="adAuthCookie" timeout="60" path="/">
  </forms>
</authentication>

4.

将下列 <authorization> 元素添加到 <authentication> 元素下。这一步的目的是只允许经过身份验证的用户访问应用程序。以前建立的 <authentication> 元素的 loginUrl 属性将未经过身份验证的请求重定向到 logon.aspx 页。

  <authorization> 
  <deny users="?" />
  <allow users="*" />
</authorization>

5.

保存 Web.config。

6.

启动 IIS Microsoft 管理控制台 (MMC) 管理单元。

7.

右键单击应用程序的虚拟目录,然后单击 Properties

8.

单击 Directory Security 选项卡,然后单击 Anonymous access and authentication control 组中的 Edit 按钮。

9.

选中 Anonymous access 复选框并清除 Allow IIS to control password 复选框。

10.

由于默认的匿名帐户 IUSR_MACHINE 没有访问 Active Directory 的权限,所以要创建一个新的最小特权帐户并在 Authentication Methods 对话框中输入帐户详细信息。

11.

单击 OK,然后再次单击 OK 关闭 Properties 对话框。

12.

返回到 Visual Studio .NET,将 <identity> 元素添加到 Web.config 中的 <authorization> 元素下并将模拟属性设置为 true。这将导致 ASP.NET 模拟稍早指定的匿名帐户。

<identity impersonate="true" />

这种配置的结果是,对应用程序的所有请求都将运行在这个已配置的匿名帐户的安全上下文中。用户将通过 Web 窗体提供凭据来针对 Active Directory 进行身份验证,但用于访问 Active Directory 的帐户将是已配置的匿名帐户。

开发在 Active Directory 中查找用户的 LDAP 身份验证代码

此过程将新的 Helper 类添加到 Web 应用程序以封装 LDAP 代码。此类最初提供一个 IsAuthenticated 方法来针对 Active Directory 用户对象验证所提供的域、用户名和密码。

要开发在 Active Directory 中查找用户的 LDAP 身份验证代码,请执行下列操作:

1.

添加一个名为 LdapAuthentication.cs 的新 C# 类文件。

2.

添加一个对 System.DirectoryServices.dll 程序集的引用。

3.

在 LdapAuthentication.cs 顶部添加下列 using 语句。

using System.Text;
using System.Collections;
using System.DirectoryServices;

4.

将现有命名空间重命名为 FormsAuthAD

5.

将两个私有字符串添加到 LdapAuthentication 类中;一个字符串存放到 Active Directory 的 LDAP 路径,另一个字符串存放用于搜索 Active Directory 的筛选器属性。

private string _path;
private string _filterAttribute;

6.

添加可用于初始化 Active Directory 路径的公共构造函数。

public LdapAuthentication(string path)
{
  _path = path;
}

7.

添加下列 IsAuthenticated 方法,此方法接受作为参数的域名、用户名和密码,并返回 bool 以指出 Active Directory 内是否存在有匹配密码的用户。此方法最初试图使用提供的凭据绑定到 Active Directory。如果此操作成功,则此方法使用 DirectorySearcher 托管类搜索指定的用户对象。如果找到,则 _path 成员被更新以指向用户对象,而 _filterAttribute 成员使用用户对象的公共名称属性进行更新。

public bool IsAuthenticated(string domain, string username, string pwd)
{
  string domainAndUsername = domain + @"/" + username;
  DirectoryEntry entry = new DirectoryEntry( _path, 
                                             domainAndUsername, pwd);

  try
  { 
    // Bind to the native AdsObject to force authentication. 
    Object obj = entry.NativeObject;
    DirectorySearcher search = new DirectorySearcher(entry);
    search.Filter = "(SAMAccountName=" + username + ")";
    search.PropertiesToLoad.Add("cn");
    SearchResult result = search.FindOne();
    if(null == result)
    {
      return false;
    }
    // Update the new path to the user in the directory
    _path = result.Path;
    _filterAttribute = (String)result.Properties["cn"][0];
  }
  catch (Exception ex)
  {
    throw new Exception("Error authenticating user. " + ex.Message);
  }
  return true;
}

开发查找用户的组成员身份的 LDAP 组检索代码

此过程扩展了 LdapAuthentication 类来提供 GetGroups 方法,此方法将检索当前用户是其成员的组列表。GetGroups 方法以管道分隔的字符串的方式返回组列表,如下所示。

"Group1|Group2|Group3|"

要开发查找用户的组成员身份的 LDAP 组检索代码,请执行下列操作:

GetGroups 方法的下列实现添加到 LdapAuthentication 类中。

public string GetGroups()
{
  DirectorySearcher search = new DirectorySearcher(_path);
  search.Filter = "(cn=" + _filterAttribute + ")";
  search.PropertiesToLoad.Add("memberOf");
  StringBuilder groupNames = new StringBuilder();
  try
  {
    SearchResult result = search.FindOne();
    int propertyCount = result.Properties["memberOf"].Count;
    String dn;
    int equalsIndex, commaIndex;

    for( int propertyCounter = 0; propertyCounter < propertyCount;
         propertyCounter++)
    {
      dn = (String)result.Properties["memberOf"][propertyCounter];

      equalsIndex = dn.IndexOf("=", 1);
      commaIndex = dn.IndexOf(",", 1);
      if (-1 == equalsIndex)
      {
        return null;
      }
      groupNames.Append(dn.Substring((equalsIndex + 1), 
                        (commaIndex - equalsIndex) - 1));
      groupNames.Append("|");
    }
  }
  catch(Exception ex)
  {
    throw new Exception("Error obtaining group names. " + ex.Message);
  } 
  return groupNames.ToString();
}

对用户进行身份验证并创建 Forms 身份验证票

此过程实现 btnLogon_Click 事件处理程序来对用户进行身份验证。对于已经过身份验证的用户,将创建一个包含用户的组列表的 Forms 身份验证票。然后,将用户重定向到这些用户请求的初始页(在重定向到登录页前)。

要对用户进行身份验证并创建 Forms 身份验证票,请执行下列操作:

1.

返回到 Logon.aspx 窗体并双击 Log On 按钮创建一个空 btnLogon_Click 事件处理程序。

2.

在文件顶部,将下列 using 语句添加到现有的 using 语句下。此操作提供对 FormsAuthentication 方法的访问权。

using System.Web.Security;

3.

添加代码以创建一个新的 LdapAuthentication 类的实例,此实例已初始化以指向 LDAP Active Directory,如下列代码所示。记住更改指向 Active Directory 服务器的路径。

// Path to you LDAP directory server.
// Contact your network administrator to obtain a valid path.
string adPath = "LDAP://yourCompanyName.com/DC=yourCompanyName,DC=com"; 
LdapAuthentication adAuth = new LdapAuthentication(adPath);

4.

添加下列代码来执行下列步骤:

根据 Active Directory 验证调用方的身份。

检索用户是其成员的组的列表。

创建包含组列表的 FormsAuthenticationTicket

对票加密。

创建包含加密票的新 cookie。

将 cookie 添加到返回给用户浏览器的 cookie 列表中。

try
{
  if(true == adAuth.IsAuthenticated(txtDomainName.Text, 
                                    txtUserName.Text, 
                                    txtPassword.Text))
  {
    // Retrieve the user's groups
    string groups = adAuth.GetGroups();
    // Create the authetication ticket
    FormsAuthenticationTicket authTicket = 
        new FormsAuthenticationTicket(1,  // version
                                      txtUserName.Text,
                                      DateTime.Now, 
                                      DateTime.Now.AddMinutes(60),
                                      false, groups);
    // Now encrypt the ticket.
    string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
    // Create a cookie and add the encrypted ticket to the 
    // cookie as data.
    HttpCookie authCookie = 
                 new HttpCookie(FormsAuthentication.FormsCookieName,
                                encryptedTicket);
    // Add the cookie to the outgoing cookies collection. 
    Response.Cookies.Add(authCookie); 

    // Redirect the user to the originally requested page
    Response.Redirect(
              FormsAuthentication.GetRedirectUrl(txtUserName.Text, 
                                                 false));
  }
  else
  {
    lblError.Text = 
         "Authentication failed, check username and password.";
  }
}
catch(Exception ex)
{
  lblError.Text = "Error authenticating. " + ex.Message;
}

实现身份验证请求处理程序来构造 GenericPrincipal 对象

此过程在 global.asax 内实现了 Application_AuthenticateRequest 事件处理程序,并为当前经过身份验证的用户创建了 GenericPrincipal 对象。这将包含用户是其成员且从包含身份验证 cookie 的 FormsAuthenticationTicket 中检索出的组的列表。最后,将 GenericPrincipal 对象与当前为每个 Web 请求创建的 HttpContext 对象关联。

要实现身份验证请求处理程序来构 GenericPricipal 对象,请执行下列步骤:

1.

使用解决方案资源管理器打开 global.asax.cs。

2.

在文件顶部添加下列 using 语句。

using System.Web.Security;
using System.Security.Principal;

3.

定位 Application_AuthenticateRequest 事件处理程序并添加下列代码,以从随请求传递的 cookie 集合中获得包含已加密 FormsAuthenticationTicket 的 cookie。

// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];

if(null == authCookie)
{
  // There is no authentication cookie.
  return;
} 

4.

添加下列代码以从 cookie 中提取和解密 FormsAuthenticationTicket

FormsAuthenticationTicket authTicket = null;
try
{
  authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch(Exception ex)
{
  // Log exception details (omitted for simplicity)
  return;
}

if (null == authTicket)
{
  // Cookie failed to decrypt.
  return; 
} 

5.

添加下列代码来解析出在最初对用户进行身份验证时附加到票上的管道分隔的组名称列表。

// When the ticket was created, the UserData property was assigned a
// pipe delimited string of group names.
String[] groups = authTicket.UserData.Split(new char[]{'|'});

6.

添加下列代码来创建一个 GenericIdentity 对象和一个 GenericPrincipal 对象。前一个对象是从票名称获得用户名,后一个对象将此标识与用户组列表包含在一起。

// Create an Identity object
GenericIdentity id = new GenericIdentity(authTicket.Name, 
                                         "LdapAuthentication");

// This principal will flow throughout the request.
GenericPrincipal principal = new GenericPrincipal(id, groups);
// Attach the new principal object to the current HttpContext object
Context.User = principal;

测试应用程序

此过程使用 Web 应用程序来请求 default.aspx 页。您将被重定向到登录页来进行身份验证。在成功进行身份验证后,浏览器将被重定向到最初请求的 default.aspx 页。此操作将从与身份验证进程所提出的当前请求相关联的 GenericPrincipal 对象中,提取并显示经过身份验证的用户所属的组的列表。

要测试应用程序,请执行下列步骤:

1.

Build 菜单上,单击 Build Solution

2.

在解决方案资源管理器中,右键单击 default.aspx,然后单击 View in Browser

3.

输入有效的域名、用户名和密码,然后单击 Log On

4.

如果您成功地进行了身份验证,则应被重定向回 default.aspx。此页上的代码应显示经过身份验证的用户的用户名。

要了解经过身份验证的用户是其成员的组的列表,请在 global.aspx.cs 文件中 Application_AuthenticateRequest 事件处理程序的末尾添加下列代码。

Response.Write("Groups: " + authTicket.UserData + "<br>");

 

阅读全文
0 0

相关文章推荐

img
取 消
img