Posted on 2006-07-28 13:51
五指魅力 阅读(2524)
评论(0) 编辑 收藏 引用
本页内容
目标
本章的目标是:
•
|
创建一个实现
IPrincipal
接口的类,该接口可与基于
.NET
角色的安全性结合在一起使用。
|
返回页首
适用范围
本章适用于以下产品和技术:
•
|
Microsoft Windows® XP
或
Windows 2000 Server (Service Pack 3)
以及更高版本的操作系统
|
•
|
.NET Framework
版本
1.0 (Service Pack 2)
和更高版本
|
•
|
Microsoft Visual C#® .NET
|
返回页首
如何使用本章内容
若要学好本章内容:
•
|
您必须具有使用
Visual C# .NET
和
Microsoft Visual Studio® .NET
进行编程的经验。
|
•
|
您必须具有使用
Visual Studio .NET
开发环境的经验。
|
•
|
您必须具有使用
ASP.NET
开发
Web
应用程序的经验。
|
•
|
阅读第
3
章身份验证和授权。这一章介绍了基于
.NET
角色的安全性并讨论了
IPrincipal
接口。
|
•
|
阅读第
8
章
ASP.NET
安全性。这一章深入介绍了
IPrincipal
以及如何在
ASP.NET Web
应用程序中使用基于
.NET
角色的安全性。
|
•
|
阅读如何将窗体身份验证用于
Active Directory
。这一章提供了有关如何从
Active Directory
获取用户帐户信息的详细信息。
|
•
|
阅读如何将窗体身份验证用于
SQL Server 2000
。这一章提供了有关如何将
Microsoft SQL Server™ 2000
用作用户帐户数据库的详细信息。
|
返回页首
摘要
Microsoft® .NET Framework
提供了两种实现
IPrincipal
接口的方法:
WindowsPrincipal
和
GenericPrincipal
。这些类提供了基于角色的授权检查功能,对于大多数应用程序方案而言,这些功能已经足够了。
但是,在某些情况下,您可能需要开发自己的
IPrincipal
实现方案,以提供自定义功能。本章介绍了如何实现自定义
IPrincipal
类以及如何在使用窗体身份验证的
ASP.NET
应用程序中将其用于基于角色的授权。
返回页首
您必须了解的背景知识
.NET Framework
提供
WindowsPrincipal
和
GenericPrincipal
类,这些类为
Windows
和非
Windows
身份验证机制提供了相应的基本角色检查功能。这两个类均实现
IPrincipal
接口。为了用于授权,
ASP.NET
要求将这些对象存储在
HttpContext.User
中。对于基于
Windows
的应用程序,必须将它们存储在
Thread.CurrentPrincipal
中。
对于大多数应用程序方案而言,这些类提供的功能已经足够了。应用程序可以显式调用
IPrincipal
.IsInRole
方法以执行编程角色检查。当使用
PrincipalPermission
类的
Demand
方法要求调用方属于某些角色(声明或强制性的)时,还会导致调用
IPrincipal.IsInRole
。
在某些情况下,您可能需要通过创建一个实现
IPrincipal
接口的类,来开发您自己的主体实现方案。可以将任何实现
IPrincipal
的类用于
.NET
授权。
实现您自己的
IPrincipal
类的原因包括:
•
|
您想扩展角色检查的功能。您可能需要一些方法来使您能够检查某一特定用户是否为多角色成员。例如:
CustomPrincipal.IsInAllRoles( "
角色
1", "
角色
2", "
角色
3" )
CustomPrincipal.IsInAnyRole( "
角色
1", "
角色
2", "
角色
3" )
|
•
|
您想实现额外的方法或属性以返回数组中角色的列表。例如:
string[] roles = CustomPrincipal.Roles;
|
•
|
您想让应用程序强制实施角色分级逻辑。例如,高级管理员在层次结构中被认为高于普通管理员。可以使用如下方法进行测试:
CustomPrincipal.IsInHigherRole("Manager");
CustomPrincipal.IsInLowerRole("Manager");
|
•
|
您想实现惰性的角色列表初始化。例如,只有在需要进行角色检查时,才能动态加载角色列表。
|
本章介绍如何实现自定义的
IPrincipal
类以及如何在使用窗体身份验证的
ASP.NET
应用程序中将其用于基于角色的授权。
返回页首
创建一个简单的
Web
应用程序
此过程创建一个新的
ASP.NET Web
应用程序。该应用程序将包含两个页面:默认页(仅允许通过身份验证的用户进行访问)和登录页(用于搜集用户凭据)。
•
|
创建一个简单的
Web
应用程序
1.
|
启动
Visual Studio .NET
,然后新建一个名为
CustomPrincipalApp
的
C# ASP.NET Web
应用程序。
|
2.
|
将
WebForm1.aspx
重命名为
Logon.aspx
。
|
3.
|
向
Logon.aspx
添加表
1
中列出的控件,创建一个登录窗体。
表
1
:
Logon.aspx
控件
控件类型
|
文本
|
ID
|
标签
|
用户名:
|
-
|
标签
|
密码
|
-
|
文本框
|
-
|
txtUserName
|
文本框
|
-
|
txtPassword
|
按钮
|
登录
|
btnLogon
|
|
4.
|
将密码文本框控件的
TextMode
属性设置为
Password
。
|
5.
|
在解决方案资源管理器中,右键单击
“CustomPrincipalApp”
,指向
“
添加
”
,然后单击
“
添加
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
类型。
身份验证代码通常涉及在自定义数据库或
Microsoft 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 ));
}
|
|
返回页首
创建一个实现和扩展
IPrincipal
的类
此过程创建一个实现
IPrincipal
接口的类。它还将其他方法和属性添加到该类中,以便提供基于角色的额外授权功能。
•
|
创建一个实现和扩展
IPrincipal
的类
1.
|
将名为
CustomPrincipal
的新类添加到当前项目中。
|
2.
|
在
CustomPrincipal.cs
的顶部添加以下
using
语句:
using System.Security.Principal;
|
3.
|
从
IPrincipal
接口派生
CustomPrincipal
类。
public class CustomPrincipal : IPrincipal
|
4.
|
将以下私有成员变量添加到该类中,以维护与当前主体和主体角色列表关联的
IIdentity
对象:
private IIdentity _identity;
private string [] _roles;
|
5.
|
修改该类的默认构造函数以接受
IIdentity
对象和角色数组。使用提供的值来初始化私有成员变量(如下所示):
public CustomPrincipal(IIdentity identity, string [] roles)
{
_identity = identity;
_roles = new string[roles.Length];
roles.CopyTo(_roles, 0);
Array.Sort(_roles);
}
|
6.
|
实现
IPrincipal
接口定义的
IsInRole
方法和
Identity
属性(如下所示):
// IPrincipal Implementation
public bool IsInRole(string role)
{
return Array.BinarySearch( _roles, role ) >= 0 ? true : false;
}
public IIdentity Identity
{
get
{
return _identity;
}
}
|
7.
|
添加以下两个公用方法以提供基于角色的扩展检查功能:
//
查看主体是否在所有指定的角色集合中
public bool IsInAllRoles( params string [] roles )
{
foreach (string searchrole in roles )
{
if (Array.BinarySearch(_roles, searchrole) < 0 )
return false;
}
return true;
}
//
查看主体是否在任意指定的角色集合中
public bool IsInAnyRoles( params string [] roles )
{
foreach (string searchrole in roles )
{
if (Array.BinarySearch(_roles, searchrole ) > 0 )
return true;
}
return false;
}
|
|
返回页首
创建
CustomPrincipal
对象
此过程实现一个应用程序身份验证事件处理程序,并基于身份验证票证中包含的信息构造
CustomPrincipal
对象来表示经过身份验证的用户。
•
|
构造
CustomPrincipal
对象
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('|');
|
6.
|
添加以下代码,使用从票证名中获取的用户名创建一个
FormsIdentity
对象,并创建一个包含此标识以及用户角色列表的
CustomPrincipal
对象:
//
创建一个标识对象
FormsIdentity id = new FormsIdentity( authTicket );
//
该主体将通过整个请求。
CustomPrincipal principal = new CustomPrincipal(id, roles);
//
将新的主体对象附加到当前的
HttpContext
对象
Context.User = principal;
|
|
返回页首
测试应用程序
此过程向
default.aspx
页中添加代码,显示附加到当前
HttpContext
对象上的
CustomPrincipal
对象中的信息,并确认已正确构造该对象并将其分配给当前的
Web
请求。它还测试新类支持的基于角色的功能。
•
|
测试应用程序
1.
|
在解决方案资源管理器中,双击
default.aspx
。
|
2.
|
双击
default.aspx Web
窗体,显示页面加载事件处理程序。
|
3.
|
滚动到文件的顶部,在现有
using
语句下面,添加以下
using
语句:
using System.Security.Principal;
|
4.
|
返回到页面加载事件处理程序,并添加以下代码以显示附加到与当前
Web
请求关联的
CustomPrincipal
上的标识名称:
CustomPrincipal cp = HttpContext.Current.User as CustomPrincipal;
Response.Write( "
已通过身份验证的标识为:
" +
cp.Identity.Name );
Response.Write( "<p>" );
|
5.
|
添加以下代码,以使用
CustomPrincipal
类支持的标准
IsInRole
方法和附加的
IsInAnyRoles
和
IsInAllRoles
方法测试当前已验证标识的角色成员身份:
if ( cp.IsInRole("Senior Manager") )
{
Response.Write( cp.Identity.Name + "
在
" + "Senior Manager
角色
"
中
);
Response.Write( "<p>" );
}
if ( cp.IsInAnyRoles("Senior Manager", "Manager", "Employee", "
Sales") )
{
Response.Write( cp.Identity.Name + "
在某个指定的
角色中
");
Response.Write( "<p>" );
}
if ( cp.IsInAllRoles("Senior Manager", "Manager", "Employee", "
Sales") )
{
Response.Write( cp.Identity.Name + "
在所有指定的
角色中
" );
Response.Write( "<p>" );
}
else
{
Response.Write( cp.Identity.Name +
"
不在所有指定的角色中
" );
Response.Write("<p>");
}
if ( cp.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
角色的成员。
|
|