表单身份验证概述

本文内容

  1. 简介
  2. 理解表单身份验证流程
  3. 步骤1:为本系列教程创建一个ASP.NET网站
  4. 步骤2:启用表单身份验证

显示另外 4 个

本文档是Visual Basic 教程(转至 Visual C# 教程

在本教程中 ,我们将从讨论转向实现 ,特别是 ,我们将探讨表单身份验证的实现。我们在本教程中开始构建的Web应用程序将在后续的教程中逐步完善,从简单的表单身份验证到成员资格和角色。

有关本主题的详细信息 ,请参见以下视频 :在 ASP.NET 中使用基本表单身份验证 。

简介

上一篇教程中 ,我们讨论了ASP.NET 提供的各种类型的身份验证、授权和用户帐户。在本教程中,我们将从讨论转向实现,特别地,我们将探讨表单身份验证的实现。我们在本教程中开始构建的Web应用程序将在后续的教程中逐步完善,从简单的表单身份验证到成员资格和角色。

本文首先深入探讨表单身份验证流程,我们在前一教程中就接触过该主题。然后,我们将创建一个ASP.NET网站,通过它来阐述表单身份验证的各个概念。接下来,我们配置该站点使用表单身份验证,创建一个简单的登录页面,考察如何编写代码来判断用户是否通过了身份验证,如果通过了身份验证,如何确定其登录时使用的用户名。

要构建一个支持用户帐户并可以通过一个Web页面来对用户进行身份验证的ASP.NET应用,这些都是必经的步骤:理解表单身份验证流程、在一个Web应用中启用表单身份验证、创建登录和注销页面。有鉴于此,并且由于这些教程是环环相扣的,我建议您在进入下一篇教程之前仔细通读本教程,即使您在以前的项目中有过配置表单身份验证的经验。

理解表单身份验证流程

当ASP.NET运行时处理一个对ASP.NET资源的请求时,比如对一个ASP.NET页面或一个ASP.NET Web 服务的请求,该请求在其生命周期内将触发许多事件。在请求最开始和最后结束时都有事件发生,在对请求进行身份验证和授权时有事件发生,在出现未处理异常时也有事件发生,不一而足。要查阅事件的完整列表,请参考HttpApplication 对象的事件

HTTP模块 是托管类,其代码作为对请求生命周期内特定事件的响应而执行。 ASP.NET 随带有多个HTTP模块,用于在后台执行一些基本的任务。其中与我们的讨论尤为相关的两个内置HTTP模块是:

  • FormsAuthenticationModule– 通过检查表单身份验证票证来验证用户的身份 , 表单身份验证票证一般包含在用户的 Cookie 集合中。如果没有表单身份验证票证,那么该用户就是匿名的。
  • UrlAuthorizationModule– 判断是否授权当前用户访问所请求的 URL 。该模块根据应用程序的配置文件中指定的授权规则来确定权限。 ASP.NET 还包含 FileAuthorizationModule,它根据所请求文件的 ACL 来确定权限。

FormsAuthenticationModuleUrlAuthorizationModule ( 和FileAuthorizationModule)执行之前尝试对用户进行身份验证。如果用户没有权限访问所请求的资源 ,则授权模块终止该请求 ,并返回一个HTTP 401 Unauthorized 状态。在Windows身份验证中,该HTTP 401 状态返回给了浏览器。该状态码导致浏览器通过一个模态对话框提示用户输入凭据。然而,在表单身份验证中,该HTTP 401 Unauthorized 状态从不返回给浏览器,因为FormsAuthenticationModule检测到该状态后,会修改该状态以便将用户重定向到登录页面(通过HTTP 302 Redirect 状态重定向)。

登录页面负责判断用户凭据是否有效 ,如果有效 ,则创建一个表单身份验证票证 ,并将用户返回到其尝试访问的那个页面。在后续的对网站页面的请求中,就包含了该表单身份验证票证, FormsAuthenticationModule 于是使用该票证来识别用户。

图01 :表单身份验证流程(单击此处查看实际大小的图像

在页面间切换时保存身份验证票证

登录后 ,每次发送请求时都要将表单身份验证票证回送给Web 服务器 ,这样 ,用户浏览站点时就会一直处于已登录状态。通常的做法是将身份验证票证存放于用户的Cookie 集合中。Cookie 是存放在用户计算机上的小文本文件。每次对创建该Cookie的网站进行请求时,都会将它放在HTTP标头中进行传递。因此,一旦创建了表单身份验证票证并将其存储在浏览器的Cookie中,后面每当再次访问该站点时,就会将该身份验证票证随同访问请求发送出去,这样用户就可以被识别出来了。

注意: 每个教程使用的演示 Web应用程序都是可以下载的。这个可下载应用程序是用Visual Web Developer 2008 创建的,面向的是.NET Framework 3.5 版本。由于应用程序面向的是.NET 3.5,其Web.config文件包含额外的3.5版本所特有的配置元素。长话短说,如果您的计算机上还没有安装.NET 3.5,下载的Web应用程序是无法运行的,除非您在Web.config中删除了3.5版本特有的标记。

对于Cookie ,要注意的一个方面是它的有效期,也就是浏览器丢弃Cookie的日期和时间。表单身份验证Cookie过期后,用户就无法通过身份验证,因而变成匿名用户。当用户在公共场所浏览站点时,他们希望浏览器关闭后身份验证票证就过期。然而在家里浏览时,同一用户可能希望系统记住其身份验证票证,这样再次打开浏览器时身份验证票证仍然存在,从而不必每次访问同一站点时都要重新登录。该决定通常由用户作出,即是否选中登录页面的“Remember me” 复选框。在步骤3中,我们将探讨如何在登录页面中实现“Remember me” 复选框。在后面的教程中,我们将详细讲述身份验证票证的超时设置。

注意: 用于登录网站的用户代理可能不支持 Cookie。在这种情况下, ASP.NET 可以使用无Cookie的表单身份验证票证。在这种模式下,身份验证票证被编码到URL中。在下一篇教程中,我们将探讨何时使用无Cookie的身份验证票证,以及如何创建和管理它们。

表单身份验证的范围

FormsAuthenticationModule是托管代码 ,是ASP.NET 运行时的一部分。在微软的Internet Information Services (IIS) Web 服务器7.0 版本之前 ,IIS的HTTP 管道和ASP.NET 运行时的管道之间有一道鸿沟。简言之, IIS 6以及更早的版本中,当请求从IIS委托给ASP.NET运行时时,才执行FormsAuthenticationModule。默认情况下 ,IIS自己处理静态内容 ,比如HTML 页面、CSS 文件、图像文件等 ,只有当请求的页面的后缀名为.aspx、.asmx 或.ashx 时才将请求转交给ASP.NET 运行时处理。

而IIS 7 可以将IIS管道和ASP.NET管道整合在一起。只需少许的配置,您就可以将IIS 7 配置为对 所有的请求都调用FormsAuthenticationModule 。此外,使用 IIS 7,您可以对任意类型的文件定义URL授权规则。详情参见IIS6 和 IIS7 安全性变化Web 平台安全性以及了解 IIS7 URL 授权

长话短说,在IIS 7 之前的版本中,表单身份验证只能用于保护ASP.NET运行时处理的资源。同样地, URL 授权也只能应用于ASP.NET运行时处理的资源。但在IIS 7 中,我们可以将 FormsAuthenticationModule 和UrlAuthorizationModule整合到IIS的HTTP管道中,从而将该功能扩展到所有的请求。

步骤1:为本系列教程创建一个ASP.NET网站

为尽可能地照顾到所有的朋友 ,在本系列教程中 ,我们将用Microsoft Visual Studio 2008 的免费版 (Visual Web Developer 2008)来构建我们的ASP.NET 网站。我们将在一个Microsoft SQL Server 2005 Express Edition 数据库中实现 SqlMembershipProvider 用户存储。如果您使用的是Visual Studio 2005,或其它版本的Visual Studio 2008 或SQL Server,不必担心,步骤几乎是相同的,而且对于比较要紧的不同之处我们会标注出来。

在可以配置表单身份验证之前 ,我们需要一个ASP.NET 网站。首先,我们创建一个新的基于文件系统的ASP.NET 网站。为此,启动Visual Web Developer,在File菜单中选择New Web Site,这将打开New Web Site 对话框。选择ASP.NET Web Site 模板,将Location下拉列表设置成File System,然后为该网站选择一个文件夹,并将语言设置成VB 。这将创建一个新的 Web 网站 ,该网站有Default.aspx ASP.NET 页面、App_Data 文件夹和Web.config 文件。

注意: Visual Studio 支持两种项目管理模式:网站项目和Web应用程序项目。网站项目没有工程文件,而Web应用程序项目则类似Visual Studio .NET 2002/2003 的项目体系结构–包括一个工程文件,并将工程的源代码编译成单个的汇编程序,将该汇编程序放置在/bin文件夹中。 Visual Studio 2005 最初只支持网站项目,后来随Service Pack 1 引入了Web应用程序项目模式。 Visual Studio 2008 支持两种项目模式。而Visual Web Developer 2005 和2008版本只支持网站项目。我使用的是网站项目模式。如果您使用的是非Express版本,且希望使用Web 应用程序项目模式,尽管用吧,只是要知道,您看到的屏幕显示和这些教程中的屏幕快照可能有些差异,您要采取的步骤和教程中给出的指示可能也有些差异。

图02:创建一个新的基于文件系统的网站 (单击此处查看实际大小的图像 )

添加一个母版页

接下来 ,在站点的根目录中添加一个名为Site.master 的母版页。利用母版页,页面开发人员可以定义一个可应用到所有ASP.NET页面的站点级模板。使用母版页的主要好处是,可以在一个位置定义站点的整体外观,从而可以容易地更新或调整站点的页面布局。

图03:向网站添加一个名为Site.master 的母版页(单击此处查看实际大小的图像

在该母版页中定义站点级页面布局。可以使用Design 视图添加任何需要的布局 控件或Web 控件 ,或者手工在Source 视图中添加标记。我仿照在 ASP.NET 2.0 中处理数据 教程系列中的页面布局来构建该母版页的布局(参见图4 )。母版页使用层叠式样式表来布置,而样式使用的是Style.css文件(本教程的相关下载中包含该文件)中定义好的CSS设置。虽然我们从下面的标记代码中读不到CSS规则的定义,但CSS规则是定义成这样的:导航<div>的内容显示于固定位置,它显示于左边,宽度固定为200象素。

<%@ Master Language="VB" CodeFile="Site.master.vb" Inherits="Site" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Forms Authentication, Authorization, and User Accounts</title> <link href="Styles.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="wrapper"> <form id="form1" runat="server"> <div id="header"> <span class="title">User Account Tutorials</span> </div> <div id="content"> <asp:contentplaceholder id="MainContent" runat="server"> <!-- Page-specific content will go here... --> </asp:contentplaceholder> </div> <div id="navigation"> TODO: Menu will go here... </div> </form> </div> </body> </html>

母版页定义的内容既包括静态页面布局 又包括一些可编辑的区域 ,这些区域可由使用该母版页的ASP.NET 页面来编辑。这些内容可编辑的区域由ContentPlaceHolder控件表示,在内容<div>中可以看到该控件。我们的母版页只有一个ContentPlaceHolder (MainContent),实际上母版页可以有多个 ContentPlaceHolder。

输入上面的标记代码后 ,切换到显示母版页布局的Design 视图。使用该母版页的所有ASP.NET页面都将显示如下的统一布局,同时这些页面能够指定MainContent区域 的标记。

图04:通过Design 视图查看到的母版页 (单击此处查看实际大小的图像

创建内容页面

现在我们的网站中有一个Default.aspx 页面 ,但它并不使用我们刚创建的母版页。虽然我们可以处理Web页面的声明标记从而使该页面使用母版页,但如果该页面尚不包含任何内容,我们这样做会更容易些:将该页面删除再重新添加进来,同时指定要使用的母版页。因此,首先从项目中删除Default.aspx 。

然后,在解决方案资源管理器中右键单击该项目名称并选择添加一个名为Default.aspx的新的Web表单。这次我们选中“Select master page” 复选框,再从列表中选择Site.master母版页。

图05:添加一个新的Default.aspx 页面并选择使用母版页(单击此处查看实际大小的图像

图06:使用Site.master 母版页(单击此处查看实际大小的图像

注意: 如果您使用的是 Web应用程序项目模式 ,Add New Item 对话框并不包含“Select master page” 复选框。因此,您需要添加一个类型为“Web Content Form” 的项。选择“Web Content Form” 选项后,单击Add ,Visual Studio 就会显示如图6所示的Select a Master 对话框。

这个新的 Default.aspx 页面的声明标记中有一个 @Page 指令,它指示定母版页文件的路径,还有一个内容控件,该控件用于母版页的 MainContent ContentPlaceHolder 。

<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" Title="Untitled Page" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server"> </asp:Content>

现在Default.aspx 页面还是空的 ,不用管它 ,我们在本教程的后面再来这里添加内容。

注意: 我们的母版页中有一个放置菜单或其它导航界面的区域。我们将在以后的教程中创建这样一个界面。

步骤2:启用表单身份验证

创建好ASP.NET 网站后 ,下一个任务是启用表单身份验证。该应用程序的身份验证配置是通过Web.config文件中的<authentication > 元素指定的。 <authentication> 元素只有一个名为mode的属性,它指定应用程序使用的身份验证模式。该属性有四个可取值:

  • Windows – 如上一篇教程所述 , 当一个应用程序使用 Windows 身份验证时 , 由 Web 服务器负责验证访问者的身份 , 且通常采用基本、摘要式或集成式 Windows 身份验证方式来进行验证。
  • Forms – 通过一个 Web 页面上的表单身份验证用户。
  • Passport – 通过微软的 Passport Network 身份验证用户。
  • None – 不采用任何身份验证模式 ; 所有的访问者都是匿名的。

默认情况下 ,ASP.NET应用程序采用的是Windows 身份验证。要将身份验证类型更改为表单身份验证,我们要将 <authentication> 元素的mode属性的值修改为Forms 。

如果您的项目尚不包含Web.config文件,在解决方案资源管理器中右键单击项目名称,选择Add New Item,添加一个Web配置文件即可。

图07:如果您的项目尚不包含Web.config,现在添加一个 (单击此处查看实际大小的图像

然后 ,定位到<authentication> 元素并将其更改为使用表单身份验证。更改完成后,您的Web.config文件的标记代码应类似如下:

<configuration> <system.web> ... Unrelated configuration settings and comments removed for brevity ... <!-- The <authentication> section enables configuration of the security authentication mode used by ASP.NET to identify an incoming user. --> <authentication mode="Forms" /> </system.web> </configuration>

注意: 由于Web.config 是一个XML 文件 ,大小写很重要。 mode 属性设置为Forms时,务必输入大写字母“F” 。如果您使用的是不同的大小写,比如“forms” ,当您在浏览器中访问站点时,会收到一个配置错误消息。

<authentication> 元素可以包含一个 <forms> 子元素,该子元素包含表单身份验证的特有设置。现在,我们只使用默认的表单身份验证设置。在下一篇教程中我们将详细探讨 <forms> 子元素。

步骤3:构建登录页面

为支持表单身份验证,我们的网站需要一个登录页面。在前面的“理解表单身份验证流程”一节中我们讲过,当用户试图未经授权而访问某个页面时, FormsAuthenticationModule 会自动地将用户重定向到登录页面。还有ASP.NET Web 控件,它会向匿名用户显示一个到登录页面的链接。问题是:“登录页面的URL是什么?”

默认情况下 ,表单身份验证系统认为登录页面为Login.aspx,位于Web 应用程序的根目录下。如果想使用另外一个URL ,可以在 Web.config文件中指定。我们将在后面的教程中探讨如何操作。

登录页面有三个职责:

  1. 提供一个界面供访问者输入其凭据。
  2. 判断提交的凭据是否有效。
  3. 通过创建表单身份验证票证使用户登录进来。

创建登录页面的用户界面

我们首先完成第一个任务。在站点根目录下创建一个名为Login.aspx 的新的ASP.NET 页面 ,套用Site.master 母版页。

图08:添加一个新的名为Login.aspx 的 ASP.NET 页面(单击此处查看实际大小的图像

典型的登录页面包含两个文本框 (分别用于输入用户名和密码 )和一个提交表单的按钮。网站通常还包含一个“Remember me” 复选框,选中它后,以后再次打开浏览器时,生成的身份验证票证依然有效。

在Login.aspx 页面上添加两个文本框 ,分别设置其ID 属性为UserName 和Password。将Password 的TextMode属性设置为Password。然后添加一个复选框控件 ,设置其ID属性为RememberMe,设置其Text属性为“Remember Me”。接着,添加一个名为LoginButton的按钮,其 Text 属性设置为“Login” 。最后,添加一个 Web标签控件,设置其 ID 属性为 InvalidCredentialsMessage,其 Text 属性为“Your username or password is invalid.Please try again.”,其 ForeColor 属性为Red ,其 Visible 属性为False 。

此时,您的屏幕看起来类似于图9 ,页面的声明代码应类似如下:

<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="Login.aspx.vb" Inherits="Login" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server"> <h1> Login </h1> <p> Username: <asp:TextBox ID="UserName" runat="server"></asp:TextBox> </p> <p> Password: <asp:TextBox ID="Password" runat="server" TextMode="Password"></asp:TextBox> </p> <p> <asp:CheckBox ID="RememberMe" runat="server" Text="Remember Me" /> </p> <p> <asp:Button ID="LoginButton" runat="server" Text="Login" OnClick="LoginButton_Click" /> </p> <p> <asp:Label ID="InvalidCredentialsMessage" runat="server" ForeColor="Red" Text="Your username or password is invalid. Please try again." Visible="False"></asp:Label> </p> </asp:Content>

图09:登录页面包含两个文本框、一个复选框、一个按钮和一个标签 (单击此处查看实际大小的图像

最后 ,为LoginButton的Click事件创建一个事件处理程序。只需在设计器中双击Button控件来创建该事件处理程序。

判断提供的凭据是否有效

现在我们需要在按钮的Click事件处理程序中完成第二个任务–判断提供的凭据是否有效。为此,我们需要有一个用户存储来保存所有用户的凭据,这样我们就可以判断提供的凭据是否与任何已知的凭据匹配。

在ASP.NET 2.0 之前,开发人员需要自己来实现用户存储并编写代码来根据用户存储身份验证提供的凭据。大多数的开发人员会在数据库中实现用户存储:创建一个名为Users的表,表的列包括UserName 、Password 、Email 、LastLoginDate等。在该数据库表,每个用户帐户对应一条记录。对用户提供的凭据进行身份验证涉及到数据库查询,看是否有某条记录的username与提供的用户名匹配,如果有,再确定数据库中相应的password是否与用户提供的密码匹配。

在ASP.NET 2.0 中 ,开发人员应使用某个Membership 提供者来管理用户存储。在本教程系列中,我们使用 SqlMembershipProvider,它使用一个SQL Server 数据库来作为用户存储。使用 SqlMembershipProvider 时,我们需要实现一个包括表、视图和存储过程的特定数据库schema 。我们将在《在 SQL Server 中创建Membership schema》教程中详细介绍该schema 。设置好 Membership 提供者后 ,为了验证用户的凭据 ,只需调用Membership 类ValidateUser(username, password) 方法 ,该方法返一个布尔值 ,指示该username 和password 组合是否有效。由于我们还未实现 SqlMembershipProvider 的用户存储,此时还不能使用Membership类的ValidateUser方法。

用不着花时间构建自定义的Users数据库表(我们实现 SqlMembershipProvider 后,该表就没有用处了),我们在登录页面中硬编码有效的凭据。在LoginButton的Click事件处理程序中,添加如下的代码:

Protected Sub LoginButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LoginButton.Click ' Three valid username/password pairs: Scott/password, Jisun/password, and Sam/password. Dim users() As String = {"Scott", "Jisun", "Sam"} Dim passwords() As String = {"password", "password", "password"} For i As Integer = 0 To users.Length - 1 Dim validUsername As Boolean = (String.Compare(UserName.Text, users(i), True) = 0) Dim validPassword As Boolean = (String.Compare(Password.Text, passwords(i), False) = 0) If validUsername AndAlso validPassword Then ' TODO: Log in the user... ' TODO: Redirect them to the appropriate page End If Next ' If we reach here, the user's credentials were invalid InvalidCredentialsMessage.Visible = True End Sub

如您所见 ,有三个有效的用户帐户– Scott、Jisun 和Sam – 它们有相同的密码(“password”)。代码对users和passwords数组进行循环处理,以找出一对与输入凭据匹配的有效用户名和密码。如果用户名和密码都有效,就要使用户登录进来并将其重定向到恰当的页面。如果凭据无效,则显示 InvalidCredentialsMessage 标签。

用户输入的凭据有效时 ,我前面提到过会将其重定向到一个 “恰当的页面 ”。可是,什么是恰当的页面呢?我们知道,当用户未经授权而访问一个页面时, FormsAuthenticationModule 会自动地将用户重定向到登录页面。在这个过程中,它将请求的URL放入ReturnUrl查询字符串参数中。也就是说,如果用户试图访问 ProtectedPage.aspx 页面,但又未被授权可以访问该页面,则FormsAuthenticationModule就会将他们重定向到:

Login.aspx?ReturnUrl=ProtectedPage.aspx

登录成功后 ,用户会被重定向到ProtectedPage.aspx 页面。另外一种情况,用户可能主动访问登录页面。在这种情况下,用户登录后,应该将他们返回到根文件夹下的Default.aspx页面。

登录用户

假定用户提供的凭据有效,我们就需要创建一个表单身份验证票证,从而使用户登录进站点。System.Web.Security 命名空间的 FormsAuthentication 类提供了很多通过表单身份验证系统登录和注销用户的方法。 FormsAuthentication 类有若干方法,但在此我们对三个方法感兴趣:

  • GetAuthCookie(username, persistCookie)– 为提供的名称 username 创建一个表单身份验证票证。接下来,该方法创建并返回一个HttpCookie 对象,该对象包含身份验证票证的内容。如果persistCookie为 True ,则创建的是一个持久的 Cookie 。
  • SetAuthCookie(username, persistCookie)– 调用 GetAuthCookie(username,persistCookie) 方法来生成表单身份验证 Cookie 。该方法将 GetAuthCookie 返回的Cookie 添加到 Cookie 集合中(假定使用的是基于 Cookie 的表单身份验证;否则,该方法调用一个内部的类来处理无Cookie 的票证逻辑)。
  • RedirectFromLoginPage(username,persistCookie) – 该方法调用 SetAuthCookie(username,persistCookie) , 然后将用户重定向到恰当的页面。

如果在将Cookie 写入到Cookie 集合之前需要修改表单身份验证票证 ,使用GetAuthCookie 方法是很方便的。如果您想创建一个表单身份验证票证并添加到Cookie集合中,但又不想将用户重定向到恰当的页面, SetAuthCookie 方法就很有用。也许您想让用户停留在登录页面上,也许您想将其导航到其它页面。

在这里我们想让用户登录进来并将他们重定向到恰当的页面,因此使用 RedirectFromLoginPage 方法。更新LoginButton的Click事件处理程序,用下面的代码替换两行TODO注释:

FormsAuthentication.RedirectFromLoginPage(UserName.Text, RememberMe.Checked)

创建表单身份验证票证时 ,用UserName 文本框的Text 属性作为表单身份验证票证的username 参数 ,用RememberMe 复选框的选中状态作为persistCookie 参数。

为了测试该登录页面,在浏览器中访问该页面。首先输入无效的凭据,比如用户名“Nope” ,密码 “wrong” 。单击 Login按钮后,将产生一个回传,并显示 InvalidCredentialsMessage 标签。

图10:输入无效凭据会显示InvalidCredentialsMessage 标签(单击此处查看实际大小的图像

接下来 ,输入有效的凭据并单击Login 按钮。这一次 ,发生回传时创建了一个表单身份验证票证 ,并自动地将您重定向到Default.aspx 页面。此时,您已经登录到网站了,虽然看不到指示您当前已登录的提示。在步骤4中,我们将探讨怎样通过编码判断用户是否已经登录,以及怎样对访问页面的用户进行识别。

步骤5将探讨从网站注销用户的技术。

保护登录页面

用户输入凭据并提交登录页面表单时,包含其密码在内的凭据以 明文的形式通过因特网传递给 Web服务器。这意味着任何正在嗅探网络的黑客都可能截获其用户名和密码。为避免此情形,有必要使用安全套接层 (SSL) 来加密网络数据流。这可确保凭据(以及整个页面的HTML标记)从离开浏览器那一刻起就已加密,直到被Web服务器所接收。

除非您的网站包含敏感信息,您只需要对登录页面和那些以明文形式传输用户密码的页面使用SSL 。您不必挂虑保护表单身份验证票证安全的事情,因为在默认情况下身份验证票证既经过加密又经过数字签名(为防止篡改)处理。在后面的教程中我们会详尽讨论表单身份验证票证的安全。

注意: 许多金融和医学网站对已验证用户可以访问的所有页面都采用了SSL 。如果您正在构建这样的网站,您可以对表单身份验证系统进行配置,使表单身份验证票证只能通过安全的连接进行传输。我们将在下一篇教程《表单身份验证配置和高级主题 》中探讨各个表单身份验证配置选项。

步骤4:检测已验证用户并确定用户标识

此时,我们已经启用了表单身份验证,并创建了一个基本的登录页面,但我们还没有讨论怎样判断用户是已验证用户还是匿名用户。在有些情况下,我们可能希望根据访问页面的用户是已验证用户还是匿名用户来显示不同的数据或信息。此外,我们还常常需要知道已验证用户的标识。

让我们对现有的Default.aspx页面进行扩充,以演示这些技术。在Default.aspx页面中添加两个面板控件,一个名为 AuthenticatedMessagePanel,另一个名为AnonymousMessagePanel 。在第一个面板中添加一个名为WelcomeBackMessage的标签控件。在第二个面板中添加一个超链接控件,设其Text属性为“Log In”,其NavigateUrl属性为 “~/Login.aspx”。此时, Default.aspx 页面的声明标记应类似如下:

<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" Title="Untitled Page" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server"> <asp:Panel runat="server" ID="AuthenticatedMessagePanel"> <asp:Label runat="server" ID="WelcomeBackMessage"></asp:Label> </asp:Panel> <asp:Panel runat="Server" ID="AnonymousMessagePanel"> <asp:HyperLink runat="server" ID="lnkLogin" Text="Log In" NavigateUrl="~/Login.aspx"></asp:HyperLink> </asp:Panel> </asp:Content>

现在您可能已经猜到了 ,想法就是对已验证用户只显示AuthenticatedMessagePanel,而对匿名用户只显示AnonymousMessagePanel。为此,我们需要根据用户是否成功登录来设置这些面板的Visible属性。

Request.IsAuthenticated属性 返回一个布尔值,指示请求是否已通过了身份验证。在Page_Load事件处理程序代码中输入如下的代码:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Request.IsAuthenticated Then WelcomeBackMessage.Text = "Welcome back!" AuthenticatedMessagePanel.Visible = True AnonymousMessagePanel.Visible = False Else AuthenticatedMessagePanel.Visible = False AnonymousMessagePanel.Visible = True End If End Sub

写好这些代码后 , 在浏览器中打开 Default.aspx 页面。假定您还没有登录,您将会看到一个到登录页面的链接(参见图 11 )。单击该链接,登录该站点。如步骤 3 所述,输入凭据后,您将返回到 Default.aspx 页面,但这次页面显示的是 “Welcome back!” 消息(参见图 12 )。

图11:匿名访问时显示一个Log In 链接(单击此处查看实际大小的图像

图12:对已验证用户显示“Welcome back!”消息 (单击此处查看实际大小的图像

我们可以通过HttpContext对象 的 User 属性来确定当前登录用户的标识。 HttpContext 对象描述了当前请求的有关信息,也包含公共ASP.NET对象: Response、 Request 以及Session等。 User 属性描述了当前HTTP请求的安全性上下文,并实现IPrincipal 接口

User属性由 FormsAuthenticationModule 设置。具体来说,当FormsAuthenticationModule在请求中找到一个表单身份验证票证,它就创建一个新的 GenericPrincipal 对象并将它赋给User属性。

Principal对象(比如GenericPrincipal )提供了用户标识以及用户所属角色的信息。 IPrincipal 接口定义了两个成员:

我们可通过下面的代码来确定当前用户的名称 :

Dim currentUsersName As String = User.Identity.Name

使用表单身份验证时 ,会为GenericPrincipal 的Identity 属性创建一个FormsIdentity 对象。 FormsIdentity 类的 AuthenticationType 属性总是返回字符串“Forms” ,IsAuthenticated属性总是返回True 。而 Name属性返回的就是创建表单身份验证票证时指定的用户名。除了这三个属性外, FormsIdentity 还有一个Ticket 属性,通过该属性可访问底层的身份验证票证。 Ticket 属性返回的是一个FormsAuthenticationTicket 类型的对象,该对象有诸如Expiration 、IsPersistent 、IssueDate 、Name等属性。

有一点要注意, FormsAuthentication.GetAuthCookie(usernamepersistCookie)、 FormsAuthentication.SetAuthCookie(usernamepersistCookie) 和FormsAuthentication.RedirectFromLoginPage( usernamepersistCookie) 方法中指定的username参数与User.Identity.Name返回的值相同。此外,这些方法创建的身份验证票证可以通过以下途径得到:将User.Identity转换为FormsIdentity对象,然后访问其Ticket属性:

Dim ident As FormsIdentity = CType(User.Identity, FormsIdentity)

Dim authTicket As FormsAuthenticationTicket = ident.Ticket

下面我们在Default.aspx 页面中提供一个更人性化的消息。更改Page_Load事件处理程序,将 WelcomeBackMessage 标签的Text属性赋值为“Welcome back, username!”:

WelcomeBackMessage.Text = "Welcome back, " & User.Identity.Name & "!"

图13 显示的是更改后的效果 (作为用户Scott 登录 )。

图13:欢迎消息中包含当前登录的用户的名称(单击此处查看实际大小的图像

使用LoginView 和LoginName 控件

对已验证用户和匿名用户显示不同的内容是一个普遍的要求 ,显示当前登录用户的名称也是如此。鉴于此, ASP.NET 有两个Web控件可提供如图13所示的功能,而我们连一行代码都不用编写。

LoginView 控件是一个基于模板的Web 控件 ,使用这个控件 ,可容易地对已验证用户和匿名用户显示不同的数据。LoginView 有两个预先定义好的模板 :

  • AnonymousTemplate – 所有添加到该模板的标记只显示给匿名用户。
  • LoggedInTemplate – 该模板的标记只显示给已验证用户。

现在我们来为站点的母版页Site.master 添加一个LoginView 控件。我们不是只添加LoginView控件,而是添加一个新的 ContentPlaceHolder 控件,然后将LoginView控件放到该ContentPlaceHolder中。不久我们就会明白这样做的原因。

注意: 除 AnonymousTemplate和LoggedInTemplate之外, LoginView 控件还可包括角色专用的模板。角色专用模板仅对那些属于指定角色的用户显示标记。我们将在后面的教程中探讨LoginView控件的基于角色的功能。

首先,在导航<div>元素中给母版页添加一个名为LoginContent的 ContentPlaceHolder 控件。您只要从工具箱中将一个ContentPlaceHolder控件拖放到源视图中,将生成的标记放在“TODO:Menu will go here…” 文本的前面。

<div id="navigation"> <asp:ContentPlaceHolder ID="LoginContent" runat="server"> </asp:ContentPlaceHolder> TODO: Menu will go here... </div>

然后 ,在LoginContent ContentPlaceHolder 中添加一个 LoginView 控件。放入母版页的 ContentPlaceHolder 控件中的内容被视为ContentPlaceHolder的默认内容。即,使用该母版页的 ASP.NET页面可为每个 ContentPlaceHolder 指定自己的内容或使用母版页的默认内容。

LoginView 和其它与登录有关的控件位于工具箱的 Login 选项卡中。

图14:工具箱中的LoginView 控件(单击此处查看实际大小的图像

接下来 ,紧接着在LoginView 控件的后面 ,但仍在ContentPlaceHolder 内 ,添加两个<br /> 元素。此时,导航<div>元素的标记代码应类似如下:

<div id="navigation"> <asp:ContentPlaceHolder ID="LoginContent" runat="server"> <asp:LoginView ID="LoginView1" runat="server"> </asp:LoginView> <br /><br /> </asp:ContentPlaceHolder> TODO: Menu will go here... </div>

可通过设计器或声明标记定义LoginView 的模板。在Visual Studio 的设计器中,展开LoginView的智能标记,其下拉列表中列出了已配置的模板。在AnonymousTemplate中输入文本“Hello, stranger”;然后,添加一个超链接控件,将其Text和NavigateUrl属性分别设置为“Log In” 和 “~/Login.aspx”。

配置好 AnonymousTemplate 模板后,切换到LoggedInTemplate ,输入文本 "Welcome back, "。然后从工具箱中将一个LoginName控件拖放到 LoggedInTemplate,将它放在"Welcome back, " 文本的后面。LoginName 控件,顾名思义,显示当前登录用户的名称。在内部实现上, LoginName 控件只是输出 User.Identity.Name 属性。

完成对LoginView模板的这些添加工作后,标记代码应类似如下:

<div id="navigation"> <asp:ContentPlaceHolder ID="LoginContent" runat="server"> <asp:LoginView ID="LoginView1" runat="server"> <LoggedInTemplate> Welcome back, <asp:LoginName ID="LoginName1" runat="server" />. </LoggedInTemplate> <AnonymousTemplate> Hello, stranger. <asp:HyperLink ID="lnkLogin" runat="server" NavigateUrl="~/Login.aspx">Log In</asp:HyperLink> </AnonymousTemplate> </asp:LoginView> <br /><br /> </asp:ContentPlaceHolder> TODO: Menu will go here... </div>

将这些添加到Site.master 母版页后 ,网站的每个页面将根据用户是否通过身份验证来显示不同的消息。图15显示的是用户Jisun通过浏览器访问Default.aspx页面的情形。“Welcome back, Jisun” 消息会重复两次:一次是在母版页左边的导航区(通过我们刚添加的LoginView控件实现),一次在Default.aspx的内容区域(通过面板控件和编程逻辑实现)。

图15:LoginView 控件显示“Welcome back, Jisun.”(单击此处查看实际大小的图像

由于我们将LoginView 添加到了母版页 ,它可以出现在站点的每个页面。然而,我们可能希望某些Web页面不显示该消息。登录页面就是这样的页面,因为在登录页面上显示到登录页面的链接看起来不合时宜。由于我们将LoginView控件放入母版页的一个 ContentPlaceHolder 控件中,我们可以在内容页中覆盖该默认的标记代码。打开Login.aspx ,进入设计器。由于我们在 Login.aspx中还未明确地给母版页的LoginContent ContentPlaceHolder 定义一个内容控件,登录页面将对该ContentPlaceHolder显示母版页的默认标记内容。在设计器中可以看到这个– LoginContent ContentPlaceHolder 显示的是默认标记内容( LoginView 控件)。

图16:登录页面显示母版页的LoginContent ContentPlaceHolder的默认内容 (单击此处查看实际大小的图像

要覆盖LoginContent ContentPlaceHolder 的默认标记内容,只需要在设计器中右键单击该区域 ,在上下文菜单中选择Create Custom Content 选项。(使用Visual Studio 2008 时, ContentPlaceHolder 包含一个智能标记,选择该标记也会提供相同的选项。)这会在页面的标记代码中添加一个新的内容控件,从而我们可以定义该页面的定制内容。您可在此处添加定制消息,比如“Please log in…”,但这里我们只是留置空白。

注意: 在 Visual Studio 2005 中,创建定制内容会在ASP.NET页面中创建一个空的内容控件。然而,在Visual Studio 2008 中,创建定制内容会将母版页的默认内容复制到新创建的内容控件。如果您使用的是Visual Studio 2008,创建了新的内容控件后,一定要清除从母版页复制过来的内容。

完成此更改后,从浏览器访问Login.aspx页面可看到图17所示的内容。注意,访问Default.aspx页面时,左边的导航<div>中并没有显示“Hello, stranger” 或“Welcome back, username” 消息。

图17:登录页面隐藏了默认的LoginContent ContentPlaceHolder的标记内容 (单击此处查看实际大小的图像

步骤5:注销

在步骤3 中 ,我们探讨了怎样构建一个登录页面让用户登录到站点 ,但我们还未讨论如何注销用户。除了提供用户登录的方法外, FormsAuthentication 类还提供了SignOut 方法。 SignOut 方法只是销毁表单身份验证票证,从而从网站中注销用户。

提供注销链接是一个常用功能,因而ASP.NET有一个专门设计来注销用户的控件。LoginStatus 控件根据用户的身份验证状态来显示“Login”链接按钮或“Logout”链接按钮。 ”Login” 链接按钮呈现给匿名用户,而“Logout”链接按钮呈现给已验证用户。可通过LoginStatus的LoginText和LogoutText属性来配置“Login”和“Logout”链接按钮的文本。

单击“Login”链接按钮会导致一个回传,进而重定向到登录页面。单击“Logout”链接按钮会导致LoginStatus控件调用FormsAuthentication.SignOff[1] 方法,然后将用户重定向到某个页面。注销用户被重定向到哪个页面取决于LogoutAction属性,该属性可以取以下三值之一:

  • Refresh – 默认值 ; 将用户重定向到他们刚访问的页面。如果他们刚访问的页面不允许匿名用户,则FormsAuthenticationModule 会自动将用户重定向到登录页面。


您可能会奇怪此处为何要进行重定向。如果用户想停留在同一页面,为何还需要显式重定向?原因是单击“Logoff”链接按钮时,用户的表单身份验证票证仍然在Cookie集合中。因此,回传请求是一个验证过的请求。 LoginStatus 控件会调用SignOut方法,但可能是在 FormsAuthenticationModule 验证了用户之后调用。因此,显式重定向会导致浏览器重新请求页面。但浏览器重新请求页面时,表单身份验证票证已删除,因此该请求是匿名的。

  • Redirect – 用户被重定向到 LoginStatus 的 LogoutPageUrl 属性中指定的 URL 。
  • RedirectToLoginPage – 用户被重定向到登录页面。

我们给母版页添加一个LoginStatus 控件 ,配置该控件使用Redirect 选项来将用户重定向到一个页面 ,该页面显示一条指示用户已注销的确认消息。首先,在根目录中创建一个名为Logout.aspx的页面,记着将该页面与Site.master母版页关联起来。然后,在该页面的标记中输入一条向用户说明他们已注销的消息。

然后,返回Site.master母版页,在LoginContent ContentPlaceHolder 中的LoginView控件下面添加一个LoginStatus控件。将该LoginStatus控件的LogoutAction属性设置为Redirect ,其 LogoutPageUrl属性设置为“~/Logout.aspx” 。


 [1] 上下文都是SignOut 方法,反馈。

<div id="navigation"> <asp:ContentPlaceHolder ID="LoginContent" runat="server"> <asp:LoginView ID="LoginView1" runat="server"> <LoggedInTemplate> Welcome back, <asp:LoginName ID="LoginName1" runat="server" />. </LoggedInTemplate> <AnonymousTemplate> Hello, stranger. <asp:HyperLink ID="lnkLogin" runat="server" NavigateUrl="~/Login.aspx">Log In</asp:HyperLink> </AnonymousTemplate> </asp:LoginView> <br /> <asp:LoginStatus ID="LoginStatus1" runat="server" LogoutAction="Redirect" LogoutPageUrl="~/Logout.aspx" /> <br /><br /> </asp:ContentPlaceHolder> TODO: Menu will go here... </div>

由于LoginStatus 控件位于LoginView 控件之外 ,所以对已验证用户和匿名用户都会显示。这样是可以的,因为LoginStatus控件会正确地显示“Login”或  “Logout”链接按钮。添加了LoginStatus控件后, AnonymousTemplate 模板中的那个“Log In” 超链接就显得多余了,因此删除它。

图18所示为Jisun访问Default.aspx时的情形。注意到左栏显示一条消息“Welcome back, Jisun” 以及一个注销链接。单击该注销链接按钮将导致一个回传,将Jisun从系统中注销,并将其重定向到Logout.aspx页面。如图19所示,当Jisun进入 Logout.aspx 页面时,她已被注销,因而变成匿名用户了。因此,左栏显示文本“Welcome, stranger” 以及一个到登录页面的链接。

图18:Default.aspx 显示 “Welcome Back, Jisun” 以及 “Logout” 链接按钮(单击此处查看实际大小的图像

图19:Logout.aspx 显示 “Welcome, stranger” 以及 “Login” 链接按钮(单击此处查看实际大小的图像

注意: 我希望您对Logout.aspx 页面进行一些处理 ,隐藏母版页的LoginContent ContentPlaceHolder(就像我们在步骤4 中对Login.aspx 的处理 )。原因是“Hello, stranger” 下面由LoginStatus控件呈现的“Login”链接按钮会将用户重定向到登录页面,并将当前的URL传入ReturnUrl查询字符串属性。简言之,如果已注销的用户单击该LoginStatus控件的“Login”链接按钮,然后登录,他们会被重定向回到Logout.aspx ,这很容易让用户感到迷惑。

小结

在本教程中,我们先探讨了表单身份验证流程,然后在一个ASP.NET应用程序中实现表单身份验证。表单身份验证由 FormsAuthenticationModule 驱动,该模块有两个职责:根据表单身份验证票证识别用户,以及将未授权用户重定向到登录页面。

.NET Framework 的FormsAuthentication类包含创建、检查和销毁表单身份验证票证的方法。 Request.IsAuthenticated 属性和User对象都支持通过编码判断请求是否通过了身份验证并确定关于用户标识的信息。另外,还有LoginView 、LoginStatus和LoginName Web 控件,使用这些控件,开发人员不用编写代码就可以快速地完成许多常见的与登录有关的任务。我们将在后面的教程中详细地探讨这些控件以及其它与登录有关的Web控件。

本教程对表单身份验证作了一个粗略的概述。我们没有探讨各个配置选项,没有探讨无Cookie的表单身份验证票证是怎样工作的,以及ASP.NET是怎样对表单身份验证票证的内容进行保护的。我们将在下一篇教程中讨论这些主题。

快乐编程!

下一篇教程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值