本页内容
目标 | |
适用范围 | |
如何使用本章内容 | |
摘要 | |
您必须了解的背景知识 | |
创建一个具有登录页的 Web 应用程序 | |
配置 Web 应用程序的窗体身份验证 | |
为通过身份验证的用户生成身份验证票证 | |
构造 GenericPrincipal 和 FormsIdentity 对象 | |
测试应用程序 | |
其他资源 |
目标
本章的目标是:
• | 创建一个 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 应用程序
|
配置 Web 应用程序的窗体身份验证
• | 编辑应用程序的 Web.config 文件,以便配置应用程序的窗体身份验证
|
为通过身份验证的用户生成身份验证票证
此过程编写代码,为通过身份验证的用户生成身份验证票证。身份验证票证是 ASP.NET FormsAuthenticationModule 使用的一种 cookie 类型。
身份验证代码通常涉及在自定义数据库或 Active Directory 中查找提供的用户名和密码。
有关执行这些查找的信息,请参见本指南中的以下章节:
• | 如何将窗体身份验证用于 Active Directory |
• | 如何将窗体身份验证用于 SQL Server 2000 |
• | 为通过身份验证的用户生成身份验证票证
|
构造 GenericPrincipal 和 FormsIdentity 对象
此过程实现一个应用程序身份验证事件处理程序,并基于身份验证票证中包含的信息构造 GenericPrincipal 和 FormsIdentity 对象。
• | 构造 GenericPrincipal 和 FormsIdentity 对象
|
测试应用程序
此过程向 default.aspx 页中添加代码,显示附加到当前 HttpContext 对象上的 GenericPrincipal 对象中的信息,并确认已正确构造该对象并将其分配给当前的 Web 请求。然后将生成和测试应用程序。
• | 测试应用程序
|
其他资源
有关详细信息,请参见本指南中的以下相关章节:
• | 如何将窗体身份验证用于 Active Directory |
• | 如何将窗体身份验证用于 SQL Server 2000 |
FormsAuthenticationTicket对象
1.使用Forms验证存储用户自定义信息
Forms验证在内部的机制为把用户数据加密后保存在一个基于cookie的票据FormsAuthenticationTicket中,因为是经过特殊加密的,所以应该来说是比较安全的。而.net除了用这个票据存放自己的信息外,还留了一个地给用户自由支配,这就是现在要说的UserData。
UserData可以用来存储string类型的信息,并且也享受Forms验证提供的加密保护,当我们需要这些信息时,也可以通过简单的get方法得到,兼顾了安全性和易用性,用来保存一些必须的敏感信息还是很有用的。
下面来看怎么使用UserData,然后会给出一个实际使用的例子。
//创建一个新的票据,将客户ip记入ticket的userdata
FormsAuthenticationTicket ticket=new FormsAuthenticationTicket(
1,userName.Text,DateTime.Now,DateTime.Now.AddMinutes(30),
false,Request.UserHostAddress);
//将票据加密
string authTicket=FormsAuthentication.Encrypt(ticket);
//将加密后的票据保存为cookie
HttpCookie coo=new HttpCookie(FormsAuthentication.FormsCookieName,authTicket);
//使用加入了userdata的新cookie
Response.Cookies.Add(coo);
下面是FormsAuthenticationTicket构造函数的重载之一的方法签名
public FormsAuthenticationTicket(
int version,
string name,
DateTime issueDate,
DateTime expiration,
bool isPersistent,
string userData
);
参数
version
版本号。
name
与身份验证票关联的用户名。
issueDate
Cookie 的发出时间。
expiration
Cookie 的到期日期。
isPersistent
如果 Cookie 是持久的,为 true;否则为 false。
userData
将存储在 Cookie 中的用户定义数据
使用userdata也很简单,FormsIdentity的Ticket属性就提供了对当前票据的访问,获得票据后就可以用UserData属性访问保存的信息,当然是经过解密的。
((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData
下面是一个具体的应用。
由于Forms验证是通过cookie来进行的,它需要传递一个票据来进行工作。虽然票据是加密的,里面的内容不可见,但这并不能阻止别人用一个假冒的身份使用票据(就像我们可以拿别人的钥匙去开别人的锁),比较常见的就是不同ip的用户在不安全通道截获了这个票据,然后使用它进行一些安全范围外的活动。
解决这个问题的办法之一就是使用SSL来传递信息。
但是如果不能使用SSL呢?我们可以判断ip和票据是否匹配,如果发出请求的ip是初次产生票据的ip,则没有问题,否则就销毁这个票据。
为此,我们需要在一开始处理登录时将用户的ip保存起来,这样就可以在以后的请求中随时验证后继请求的ip是否和初始ip相同。保存这个敏感ip的最佳场所当然是UserData啦,而验证的时机则是在AuthenticateRequest事件发生时,即Global.aspx.cs中定义的处理此事件的Application_AuthenticateRequest方法中。
上面的示例实际上已经是把用户ip保存到了UserData中,下面是验证的过程。
if(this.Request.IsAuthenticated)
{
if(((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData !=this.Request.UserHostAddress)
{
System.Security.Principal.GenericIdentity gi=new System.Security.Principal.GenericIdentity("","");
string[] rolesi={};
System.Security.Principal.GenericPrincipal gpi=new System.Security.Principal.GenericPrincipal(gi,rolesi);
this.Context.User=gpi;
}
}
通过给GenericPrincipal空的GenericIdentity和roles使票据失效,这样将强迫用户重新登录。为了测试这个方法,可以先把条件改为相等,看效果如何 :)
这个方法也有不足之处,具体为:
1.使用同一代理的用户将拥有同一个ip,这样就不能防范此类假冒攻击了
2.如果用户使用动态ip,则可能造成正常用户被我们强行销毁票据。不过总的来说,这个办法还是比较可行的。
FormsAuthenticationTicket基于forms的验证
构建基于forms的验证机制过程如下:
1,设置IIS为可匿名访问和asp.net web.config中设置为form验证
2,检索数据存储验证用户,并检索角色(如果不是基于角色可不用)
3,使用FormsAuthenticationTicket创建一个Cookie并回发到客户端,并存储
角色到票据中,如:
FormsAuthentication.SetAuthCookie(Username,true | false)
cookies保存时间:
HttpContext.Current.Response.Cookies[FormsAuthentication.FormsCookieName].Expires=DateTime.Now.AddDays(1)
如果需要存储角色,采用:
FormsAuthenticationTicket authTicket = new
FormsAuthenticationTicket(
1, // 版本号。
txtUserName.Text, // 与身份验证票关联的用户名。
DateTime.Now, // Cookie 的发出时间。
DateTime.Now.AddMinutes(20),// Cookie 的到期日期。
false, // 如果 Cookie 是持久的,为 true;否则为 false。
roles ); // 将存储在 Cookie 中的用户定义数据。
roles是一个角色字符串数组
string encryptedTicket = FormsAuthentication.Encrypt(authTicket); //加密
存入Cookie
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket);
Response.Cookies.Add(authCookie);
4,在Application_AuthenticateRequest事件中处理程序中(Global.asax)中,使用
票创建IPrincipal对象并存在HttpContext.User中
代码:
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);//解密
string[] roles = authTicket.UserData.Split(new char[]{';'});//根据存入时的格式分解,;或|....
Context.User = new GenericPrincipal(Context.User.Identity, Roles);//存到HttpContext.User中
判断某个角色验证
HttpContext.Current.User.IsInRole(roles)
具体实现
Web.config文件
加入节点,name为COOKIE名称,loginUrl为没有通过验证跳转的地址
<system.web>
<authentication mode="Forms">
<forms name="Hstear"
loginUrl="login.aspx" protection="All" path="/" timeout="40"/>
</authentication>
</system.web>
设置目录访问 path为目录名,roles为票据中的角色名
发现网上的都说要单独一个WEB.CONFIG文件放在目录中,但实际在根目录中设置即可,单个文件也一样
<location path="Admin">
<system.web>
<authorization>
<allow roles="admin"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Global.asax文件
Application_AuthenticateRequest事件中加入
protected void Application_AuthenticateRequest(Object sender, EventArgs e) { string cookieName = FormsAuthentication.FormsCookieName; HttpCookie authCookie = Context.Request.Cookies[cookieName]; FormsAuthenticationTicket authTicket = null; try { authTicket = FormsAuthentication.Decrypt(authCookie.Value); } catch(Exception ex) { return; } string[] roles = authTicket.UserData.Split(new char[]{','});//如果存取多个角色,我们把它分解 FormsIdentity id = new FormsIdentity( authTicket ); GenericPrincipal principal = new GenericPrincipal(id, roles); Context.User =principal;//存到HttpContext.User中 }
原理,将用户角色信息保存在票据中,通过Global.asax,WEB.CONFIG中的设置,判断角色的权限