通过FormsAuthentication类实现自动登录

本页内容
目标目标
适用范围适用范围
如何使用本章内容如何使用本章内容
摘要摘要
您必须了解的背景知识您必须了解的背景知识
创建一个具有登录页的 Web 应用程序创建一个具有登录页的 Web 应用程序
配置 Web 应用程序的窗体身份验证配置 Web 应用程序的窗体身份验证
为通过身份验证的用户生成身份验证票证为通过身份验证的用户生成身份验证票证
构造 GenericPrincipal 和 FormsIdentity 对象构造 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 应用程序

1.

启动 Visual Studio .NET,然后创建一个名为 GenericPrincipalApp 的新的 Visual C# ASP.NET Web 应用程序。

2.

将 WebForm1.aspx 重命名为 Logon.aspx。

3.

向 Logon.aspx 中添加以下控件,创建登录窗体。

表 1:Logon.aspx 控件

控件类型文本ID

标签

用户名:

-

标签

密码

-

文本框

-

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> 元素的子元素,并按以下所示设置 loginUrlnametimeout 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

 

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中的设置,判断角色的权限

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值