本页内容
目标
本章的目标是:
• | 创建一个 Web 应用程序,该应用程序使用窗体身份验证,并用通过身份验证的用户的标识和角色填充 GenericPrincipal 对象,以便进行基于 .NET 角色的授权。 |
适用范围
本章适用于以下产品和技术:
• | Microsoft Windows® XP 或 Windows 2000 Server (Service Pack 3) 以及更高版本的操作系统 |
• | Microsoft .NET Framework 版本 1.0 (Service Pack 2) 以及更高版本 |
• | Microsoft Visual Studio® 1.0 .NET 以及更高版本 |
• | Microsoft Visual C#® .NET |
如何使用本章内容
若要学好本章内容:
• | 您必须具有使用 Visual C# .NET 和 Visual Studio .NET 进行编程的经验。 |
• | 您必须具有使用 Visual Studio .NET 开发环境的经验。 |
• | 您必须具有使用 ASP.NET 开发 Web 应用程序的经验。 |
• | 阅读第 3 章身份验证和授权。这一章介绍了基于 .NET 角色的安全性并讨论了 GenericPrincipal 对象。 |
• | 阅读第 8 章 ASP.NET 安全性。这一章广泛介绍了 GenericPrincipal 以及如何在 ASP.NET Web 应用程序中使用基于 .NET 角色的安全性。 |
• | 阅读如何将窗体身份验证用于 Active Directory。这一章提供了有关如何从 Active Directory 获取用户帐户信息的详细信息。 |
• | 阅读如何将窗体身份验证用于 SQL Server 2000。这一章提供了有关如何将 SQL Server 2000 用作用户帐户数据库的详细信息。 |
摘要
在使用不基于 Windows 帐户的自定义身份验证机制时,要在 Web 应用程序中使用基于 Microsoft® .NET 角色的安全性,则必须自己动手创建 ASP.NET 身份验证票证,并配置 IPrincipal 实例。它们代表通过身份验证的用户的标识和角色。
本章介绍如何创建使用窗体身份验证的 Web 应用程序,以验证用户的身份和创建包含用户和角色信息的身份验证票证。其中还介绍了如何将此信息映射到 GenericPrincipal 和 FormsIdentity 对象中,以便将它们用于应用程序中的授权逻辑。
您必须了解的背景知识
使用窗体身份验证的应用程序通常需要使用 GenericPrincipal 类(与 FormsIdentity 类结合使用),创建独立于 Windows 域的非 Windows 特定的授权方案。
例如,应用程序可以:
• | 使用窗体身份验证以获取用户凭据(用户名和密码)。 |
• | 针对数据存储验证提供的凭据;例如,数据库或 Microsoft Active Directory® 目录服务。 |
• | 基于从数据存储中检索的值创建 GenericPrincipal 和 FormsIdentity 对象。它们可能包含用户角色的成员身份细节。 |
• | 使用这些对象进行授权决策。 |
本章介绍如何创建基于窗体的 Web 应用程序,以验证用户的身份和创建包含用户和角色信息的自定义窗体身份验证票证。其中还介绍了如何将此信息映射到 GenericPrincipal 和 FormsIdentity 对象中,并将这些新对象与 HTTP Web 请求上下文关联起来,以便将它们用于应用程序中的授权逻辑。
本章侧重于介绍如何构造 GenericPrincipal 和 FormsIdentity 对象以及处理窗体身份验证票证。有关如何针对 Active Directory 和 SQL Server 2000 验证用户身份的详细信息,请参见本指南中的以下相关章节:
• | 如何将窗体身份验证用于 Active Directory |
• | 如何将窗体身份验证用于 SQL Server 2000 |
创建一个具有登录页的 Web 应用程序
此过程创建一个新的 ASP.NET Web 应用程序。该应用程序将包含两个页面:默认页(仅允许通过身份验证的用户进行访问)和登录页(用于搜集用户凭据)。
• | 创建一个具有登录页的 Web 应用程序
1. | 启动 Visual Studio .NET,然后创建一个名为 GenericPrincipalApp 的新的 Visual C# ASP.NET Web 应用程序。 | 2. | 将 WebForm1.aspx 重命名为 Logon.aspx。 | 3. | 向 Logon.aspx 中添加以下控件,创建登录窗体。 表 1:Logon.aspx 控件
标签 | 用户名: | - | 标签 | 密码 | - | 文本框 | - | txtUserName | 文本框 | - | txtPassword | 按钮 | 登录 | btnLogon |
| 4. | 将密码文本框控件的 TextMode 属性设置为 Password。 | 5. | 在解决方案资源管理器中,右键单击“GenericPrincipalApp”,指向“添加”,然后单击“添加 Web 窗体”。 | 6. | 输入“default.aspx”作为新的窗体名,然后单击“打开”。 |
|
配置 Web 应用程序的窗体身份验证
• | 编辑应用程序的 Web.config 文件,以便配置应用程序的窗体身份验证
1. | 使用解决方案资源管理器打开 Web.config。 | 2. | 查找 <authentication> 元素,并将 mode 属性更改为 Forms。 | 3. | 将 <forms> 元素添加为 <authentication> 元素的子元素,并按以下所示设置 loginUrl、name、timeout 和 path 属性: <authentication mode="Forms">
<forms loginUrl="logon.aspx" name="AuthCookie" timeout="60" path="/">
</forms>
</authentication>
| 4. | 在 <authentication> 元素下面添加以下 <authorization> 元素。这样将只允许通过身份验证的用户访问该应用程序。先前为 <authentication> 元素建立的 loginUrl 属性会将未通过身份验证的请求重定向到 Logon.aspx 页。 <authorization>
<deny users="?" />
<allow users="*" />
</authorization>
|
|
为通过身份验证的用户生成身份验证票证
此过程编写代码,为通过身份验证的用户生成身份验证票证。身份验证票证是 ASP.NET FormsAuthenticationModule 使用的一种 cookie 类型。
身份验证代码通常涉及在自定义数据库或 Active Directory 中查找提供的用户名和密码。
有关执行这些查找的信息,请参见本指南中的以下章节:
• | 如何将窗体身份验证用于 Active Directory |
• | 如何将窗体身份验证用于 SQL Server 2000 |
• | 为通过身份验证的用户生成身份验证票证
1. | 打开 Logon.aspx.cs 文件,并将下面的 using 语句添加到文件顶部的现有 using 语句的下面: using System.Web.Security;
| 2. | 将以下私有帮助器方法添加到名为 IsAuthenticated 的 WebForm1 类中,该类用于验证用户名和密码,从而验证用户的身份。该代码假设所有用户名和密码的组合都有效。 private bool IsAuthenticated( string username, string password )
{
// 查找为清楚起见而省略的代码
// 此代码通常将针对 SQL 数据库或 Active Directory
// 验证用户名和密码组合
// 模拟已通过身份验证的用户
return true;
}
| 3. | 添加以下名为 GetRoles 的私有帮助器方法,该方法用于获取用户所属的角色集: private string GetRoles( string username, string password )
{
// 查找为清楚起见而省略的代码
// 此代码通常将从数据库表中查找
角色列表。
// 如果用户已经为 Active Directory 进行了身份验证,则可能使用
// 该用户所属的安全组和/或通讯
// 组列表
// 此 GetRoles 方法返回一个包含角色的以管道符分隔的
字符串,
// 而不是返回一个数组,因为字符串格式便于
// 存储在身份验证票证/cookie 中,就像用户数据那样
return "Senior Manager|Manager|Employee";
}
| 4. | 在“设计器”模式下显示 Logon.aspx 窗体,然后双击“登录”按钮以创建一个单击事件处理程序。 | 5. | 添加一个对 IsAuthenticated 方法的调用,提供通过登录窗体捕获的用户名和密码。将返回值分配给一个布尔型变量,指示用户是否通过身份验证。 bool isAuthenticated = IsAuthenticated( txtUserName.Text,
txtPassword.Text );
| 6. | 如果用户通过身份验证,则添加一个对 GetRoles 方法的调用来获取用户的角色列表。 if (isAuthenticated == true )
{
string roles = GetRoles( txtUserName.Text, txtPassword.Text );
| 7. | 创建新的窗体身份验证票证,它包含用户名、到期时间和该用户所属的角色列表。请注意,身份验证票证的用户数据属性用于存储该用户的角色列表。另请注意,以下代码创建了一个非永久性票证。不过,决定票证或 cookie 是否永久取决于您的应用程序方案。 // Create the authentication ticket
FormsAuthenticationTicket authTicket = new
FormsAuthenticationTicket(1, // 版本
txtUserName.Text, // 用户名
DateTime.Now, // 创建
DateTime.Now.AddMinutes(60),// 到期
false, // 永久
roles ); // 用户数据
| 8. | 添加代码,为该票证创建一个加密的字符串表示,并将它作为数据存储在 HttpCookie 对象内。 // 现在对票证进行加密。
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
// 创建一个 cookie 并将加密的票证添加到
// 该 cookie 作为数据。
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket);
| 9. | 将 cookie 添加到返回给用户浏览器的 cookie 集合中。 // 将该 cookie 添加到传出 cookie 集合。
Response.Cookies.Add(authCookie);
| 10. | 将用户重定向到最初请求的页面。 // 将用户重定向到最初请求的页面
Response.Redirect( FormsAuthentication.GetRedirectUrl(
txtUserName.Text,
false ));
}
|
|
构造 GenericPrincipal 和 FormsIdentity 对象
此过程实现一个应用程序身份验证事件处理程序,并基于身份验证票证中包含的信息构造 GenericPrincipal 和 FormsIdentity 对象。
• | 构造 GenericPrincipal 和 FormsIdentity 对象
1. | 从解决方案资源管理器中,打开 global.asax。 | 2. | 切换到代码视图,并在文件的顶部添加以下 using 语句: using System.Web.Security;
using System.Security.Principal;
| 3. | 找到 Application_AuthenticateRequest 事件处理程序并添加以下代码,以便从随请求一起传输的 cookie 集合中获取窗体身份验证 cookie: // 提取窗体身份验证 cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if(null == authCookie)
{
// 没有身份验证 cookie。
return;
}
| 4. | 添加以下代码,从窗体身份验证 cookie 中提取身份验证票证并进行解密: FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch(Exception ex)
{
// 记录异常情况详细信息(为简便起见,已省略)
return;
}
if (null == authTicket)
{
// 无法解密 Cookie。
return;
}
| 5. | 添加以下代码,解析当用户最初通过身份验证后附加到票证的以管道符分隔的角色名列表: // 创建票证后,为 UserData 属性指定一个
// 以管道符分隔的角色名字符串。
string[] roles = authTicket.UserData.Split(new char[]{'|'});
| 6. | 添加以下代码,使用从票证名中获取的用户名创建一个 FormsIdentity 对象,并创建一个包含此标识以及用户角色列表的 GenericPrincipal 对象: // 创建一个标识对象
FormsIdentity id = new FormsIdentity( authTicket );
// 该主体将通过整个请求。
GenericPrincipal principal = new GenericPrincipal(id, roles);
// 将新的主体对象附加到当前的 HttpContext 对象
Context.User = principal;
|
|
测试应用程序
此过程向 default.aspx 页中添加代码,显示附加到当前 HttpContext 对象上的 GenericPrincipal 对象中的信息,并确认已正确构造该对象并将其分配给当前的 Web 请求。然后将生成和测试应用程序。
• | 测试应用程序
1. | 在解决方案资源管理器中,双击 default.aspx。 | 2. | 双击 default.aspx Web 窗体,显示页面加载事件处理程序。 | 3. | 滚动到文件的顶部,在现有 using 语句下面,添加以下 using 语句: using System.Security.Principal;
| 4. | 返回到页面加载事件处理程序,并添加以下代码以显示附加到与当前 Web 请求关联的 GenericPrincipal 上的标识名称: IPrincipal p = HttpContext.Current.User;
Response.Write( "已通过身份验证的标识为:" +
p.Identity.Name );
Response.Write( "<p>" );
| 5. | 添加以下代码,测试当前通过身份验证的标识的角色成员身份: if ( p.IsInRole("Senior Manager") )
Response.Write( "用户在 Senior Manager 角色中<p>" );
else
Response.Write( "用户不在 Senior Manager 角色中<p>" );
if ( p.IsInRole("Manager") )
Response.Write( "用户在 Manager 角色中<p>" );
else
Response.Write( "用户不在 Manager 角色中<p>" );
if ( p.IsInRole("Employee") )
Response.Write( "用户在 Employee 角色中<p>" );
else
Response.Write( "用户不在 Employee 角色中<p>" );
if ( p.IsInRole("Sales") )
Response.Write( "用户在 Sales 角色中<p>" );
else
Response.Write( "用户不在 Sales 角色中<p>" );
| 6. | 在解决方案资源管理器中,右键单击 default.aspx,然后单击“设为起始页”。 | 7. | 在“生成”菜单上,单击“生成解决方案”。排除任何编译错误。 | 8. | 按 Ctrl+F5 运行应用程序。因为 default.aspx 已配置为起始页,所以这是最初请求的页面。 | 9. | 当您被重定向到登录页(因为您最初并没有身份验证票证)时,请输入用户名和密码(输入什么都可以),然后单击“登录”。 | 10. | 确认您被重定向到 default.aspx,并显示了用户标识和正确的角色细节。用户应该是 Senior Manager、Manager 和 Employee 角色的成员,而不应是 Sales 角色的成员。 |
|
其他资源
有关详细信息,请参见本指南中的以下相关章节:
• | 如何将窗体身份验证用于 Active Directory |
• | 如何将窗体身份验证用于 SQL Server 2000 |