Security Tutorials系列文章第二章:Forms Authentication概述
Security Tutorials系列文章第二章:Forms Authentication概述
本文英文原版及代码下载:
http://www.asp.net/learn/security/tutorial-02-cs.aspx
导言:
在上一篇文章里,我们探讨了ASP.NET提供了各种不同的authentication, authorization,以及 user account.在本文我们将考察forms authentication的执行.我们在本文构建的web应用程序将在以后逐步完善,从forms authentication扩展到membership 和 roles.
本文我们首先深入的探讨表单认证流程.然后,我们将创建一个ASP.NET站点,通过它来阐述forms authentication的概念.接下来,我们配置该站点使用forms authentication,创建一个简单的登陆页面,看如何写代码判断用户是否通过了认证.要构建一个支持用户帐户并可以通过一个web page来对用户进行认证的ASP.NET应用程序的话,这些都是很重要的:理解表单认证并在一个web应用程序里运用、创建登陆和注销页面.因为这些系列文章都是相互关联的,因此我建议你依次阅读该系列,即使你在以前的项目里对配置表单验证已经很有经验了.
理解表单验证流程
当ASP.NET runtime处理一个对ASP.NET资源——比如ASP.NET page或ASP.NET Web service的请求时,在该请求的生命周期里将引发一系列的事件,贯彻请求的开始和结束。要查阅这些完整的事件列表请参阅《HttpApplication object’s events》HTTP Module是托管类(managed classes),用于处理在请求生命周期里的特定事件.ASP.NET里有多个HTTP Module,用于在后台执行一些基本的任务.其中与本系列尤其相关的2个内置HTTP Module是:
.FormsAuthenticationModule——通过检查表单认证票据(forms authentication ticket)来认证用户.表单认证票据一般是包含在用户的cookies collection里的.如果没有表单认证票据,那么这个用户就是匿名的.
.UrlAuthorizationModule——判断是否授权当前用户访问所请求的URL.该module需要根据应用程序的配置文件指定的授权规则来执行.当然,
ASP.NET里也包含FileAuthorizationModule——它要根据所请求文件的ACLs来决定是否给与权限.
FormsAuthenticationModule在UrlAuthorizationModule(以及FileAuthorizationModule)执行之前运行.如果用户请求未被授权,那么就终止该请求,并返回一个HTTP 401 Unauthorized status.在Windows authentication里,该HTTP 401 status是要返回给浏览器的,使浏览器呈现一个modal对话框,要求用户输入相关认证信息.然而,在forms authentication里,该HTTP 401 Unauthorized status并不会返回给浏览器,因为FormsAuthenticationModule探测到该状态后,(通过HTTP 302 Redirect status)直接将用户导航到登陆页面.
登陆页面负责判断用户是否通过了认证,如果通过了则创建一个表单认证票据,并将用户再返回到他期望访问的那个页面.再以后对页面的请求里,就包含了该表单认证票据,便于FormsAuthenticationModule用来对用户进行甄别.
图1
在页面间导航时保存认证票据(Authentication Ticket)登陆后,每次发送请求时该表单认证票据都要返回给web服务器,这样当用户在浏览站点时他们就处于登陆状态.通常的做法时将认证票据放在用户的cookies collection里.Cookies是存放在客户端电脑上的很小的text文件.每次对站点的请求都将认证票据放在HTTP headers里进行传递.因此,一旦创建了表单认证票据并存储在浏览器的cookies里,随后的请求里就包含了表单认证票据,这样就可以甄别用户了.
使用cookies要注意的一方面是它的有效期,也就是浏览器什么时候销毁cookie.当表单认证票据过期失效后,用户就无法通过认证而变成匿名用户.当用户在公共场所浏览站点时,他们希望关闭浏览器后cookies就过期,然而在家里浏览时,他们希望再次登陆时表单认证票据仍然有效而不必再次登陆.这个问题我们可以通过在登陆页面添加一个“Remember me” checkbox来解决.在第3步里我们将考察如何实现.在后面我们将详细探讨表单认证票据的timeout设置问题.
注意:
客户端有可能不支持cookies.在这种情况下,ASP.NET也可以使用无cookie的表单认证票据。在这种模式下,将表单认证票据编码进URL.在下一篇文章我们来看在什么时候使用无cookie的表单认证票据,以及如何创建和管理.
The Scope of Forms Authentication
FormsAuthenticationModule是“托管的”,且是ASP.NET runtime的一部分。在IIS7之前的版本,IIS的HTTP pipeline和ASP.NET runtime的pipeline之间有一道鸿沟.简单的说,IIS 6以及更早的版本,当请求是从IIS委托到ASP.NET runtime时,才执行FormsAuthenticationModule.
默认情况下,IIS只处理静态的内容——比如HTML页面、CSS、image文件等.当请求的资源的后缀名为.aspx, .asmx,或.ashx是才将请求转交给ASP.NET runtime处理.
IIS 7则不同,它允许对IIS pipelines和ASP.NET pipelines进行整合.只需少许的配置你就可以使IIS 7对所有的请求都调用FormsAuthenticationModule.此外,使用IIS 7,你可以对任意类型的文件定义URL authorization rules.更多详情请参阅文章《Changes Between IIS6 and IIS7 Security》、《Forms Authentication in IIS7》、《Understanding IIS7 URL Authorization》.简而言之,在IIS 7之前,要想使用forms authentication来保护请求的资源你只能通过ASP.NET runtime,同样也只能通过ASP.NET runtime才能对资源应用URL authorization rules.但在IIS 7里,我们可以将FormsAuthenticationModule 和 UrlAuthorizationModule整合进IIS的HTTP pipeline.
第一步:为本系列文章创建一个ASP.NET Website
为了尽可能的照顾到所有的朋友,我们将用Microsoft Visual Studio 2008的免费版——Visual Web Developer 2008来构建我们的站点,SqlMembershipProvider用户存储使用的是Microsoft SQL Server 2005 Express Edition数据库.如果你使用的是Visual Studio 2005或Visual Studio 2008 或 SQL Server的其它版本,没关系,步骤都差不多,在有差异的地方我们会标注出来.
注意:
每章的代码都可在每章下载,代码是用Visual Web Developer 2008创建的,面向的是.NET Framework 3.5版本,其Web.config文件包含了额外的3.5-specific配置元素.长话短说,如果您还没有安装.NET 3.5,下载的代码是无法运行的,除非你在Web.config里删除3.5-specific markup.
在配置forms authentication前,我们需要先创建一个ASP.NET站点.启动Visual Web Developer,在File菜单里选New Web Site,这将打开New Web Site对话框.选ASP.NET Web Site模板,在Location下拉列表里选File System,选择一个文件夹来放置该站点,选语言为C#.这就创建了一个包含Default.aspx页面、一个App_Data文件夹和一个Web.config文件.
注意:
Visual Studio这些2种project管理模式:Web Site Projects 以及 Web Application Projects.其中Web Site Projects没有project文件,
而Web Application Projects则模仿Visual Studio .NET 2002/2003的project体系,包含一个project文件,并将项目的源代码编译进一个装配件,放置在/bin文件夹里.Visual Studio 2005最初只支持Web Site Projects,尽管在Service Pack 1里引入了Web Application Project模式;而Visual Web Developer 2005和2008 版只支持Web Site Projects, 我使用的就是Web Site Project模式,如果你使用的是非Express版本的,且希望使用Web Application Project模式,尽管用吧,只是你看到的画面和本系列的截屏可能有些不太一样,你采取的步骤和本文的要注意区别.
图2
添加一个母版页
接下来在站点的根目录添加一个母版页,名为Site.master.Master page使得页面开发人员定义一个站点模板,以运用到 ASP.NET页面.主要的好处是,可以在定义站点的总体界面,很容易的更新或拼装站点布局.
图3.向站点添加一个名为Site.master的母版页
在母版页定义站点级的布局.你可以在视图模式里添加任何你需要的布局或控件,或直接在源码模式里添加代码来实现.我仿照的是《Working with Data in ASP.NET 2.0 tutorial》系列文章里运用的布局(见图4).母版页使用cascading style sheets来布置,而样式使用的是Style.css文件里定义好的CSS设置(它包含在相关的下载里).
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="Site.master.cs" 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" _fcksavedurl=""Styles.css"" _fcksavedurl=""Styles.css"" _fcksavedurl=""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控件来划定,我们的母版页使用的是单独的一个ContentPlaceHolder控件 (MainContent), 实际上母版页可以包含多个ContentPlaceHolders.
输入上述代码后,我们切换到视图模式看母版页的布局。任何运用该母版页的ASP.NET都有统一的布局,且可以在MainContent控件的区域内进行编辑.
图4:视图模式下的母版页
创建内容页面
现在我们有一个Default.aspx页面,但它并没有使用我们刚创建的母版页。虽然我们可以显式地声明代码以使一个web页面使用母版页面.但如果该页面还没有包含任何的内容时,我们可以将其删除再重新添加进来,指定它运用母版页,因此将Default.aspx页面删除.接下来,在资源管理器里的project名称上右键单击选添加一个Web Form,名为Default.aspx.此时,选“Select master page” checkbox,再在下拉列表里选Site.master母版页.
图5:添加一个新的Default.aspx页面并选择运用母版页
图6:使用Site.master
注意:
如果你使用的是Web Application Project模式,Add New Item对话框并不包含“Select master page” checkbox.因此,你需要添加一个类型为“Web Content Form”的项,选中“Web Content Form” 选项后,单击Add,Visual Studio就会像图6那样显示一个相同的Select a Master对话框.
该Default.aspx页面的声明代码里包含一个@Page标签,指定了母版页的路径,以及一个Content
控件,如下:
<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"
Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">
</asp:Content>
现在不用管该Default.aspx页面,我们在后面要添加内容.
注意:
我们的母版页将有一个区域来包含一个菜单,或其它的导航界面.我们将在以后的文章创建该界面
第2步:启用Forms Authentication
创建好站点后,下一步是启用forms authentication.应用程序的authentication配置是在Web.config文件里的<authentication>元素里指定的,该<authentication>元素包含一个名为mode的属性,它指定了应用程序使用的authentication模式,该属性的值可为几种之一:
.Windows——正如前面的文章探讨的那样,当一个应用程序使用Windows authentication时,web server负责对访问者甄别,且通常通过Basic, Digest,或Integrated Windows authentication.
.Forms——用户通过一个web页面里的一个form进行甄别
.Passport——用户通过微软的Passport Network进行甄别.
.None——不采用任何的authentication模式,所有的访问者都是匿名的.
默认情况下,ASP.NET应用程序采用的是Windows authentication. 我们需要将<authentication>元素的mode改为Forms即可.如果你的project并不包含Web.config文件,只需在解决方案管理器里添加一个即可:
图7:添加一个Web.config文件
然后定位到<authentication>元素将其改为使用forms 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文件,改为Forms时,务必输入字符“F”.如果你输入“forms”的话, 当你在浏览器里登陆时,你会收到一个配置错误.
该<authentication>元素可能还包含有一个<forms>自元素,包含了与authentication相关的设置.现在我们使用默认的forms authentication设置.我们将在后面的文章里详细的对<forms>子元素进行扩展.
第3步:创建Login Page
为了使我们的站点支持forms authentication,我们需要一个login page,就像在前面的“理解表单验证流程”部分探讨的那样,如果用户未被授权,FormsAuthenticationModule将自动的把用户导航到login page,也有ASP.NET Web控件将展示一个把匿名用户导航到login page的链接,问题是:“login page的URL是多少呢?”.
默认情况下,在forms authentication system里login page为Login.aspx,且位于web应用程序的根目录下.如果你像使用另外一个URL,你可以在Web.config文件里指定.我们将在后面的文章里探讨如何操作.
login page有3个职责:
1.提供一个界面供用户登录
2.判断用户提交的登录信息是否有效
3.通过创建表单验证票据使用户处于登录状态
创建Login Page的用户界面
在站点根目录创建一个新的页面名为Login.aspx,套用Site.master母版页.
图8.添加一个名为Login.aspx的新页面
典型的登录页面由2个textboxes构成,分别用于输入用户名和密码,另外还有一个按钮用于提交表单.通常,还包含一个“Remember me” checkbox,选中它后,以后再次打开浏览器时,先前的票据依然有效.
在Login.aspx页面上添加2个TextBox,分别设其ID为UserName 和 Password,并将Password的TextMode属性设为Password.然后添加一个CheckBox控件,设其ID为RememberMe,设其Text属性为“Remember Me”.接着添加一个名为LoginButton的Button,设其Text属性为“Login”.
最后,添加一个名为InvalidCredentialsMessage的Label控件,其Text属性为 “Your username or password is invalid. Please try again.”, 设其ForeColor属性为Red,Visible属性为False.
这样你的屏幕看起来和图9差不多,页面的声明代码和下面的差不多:
<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"CodeFile="Login.aspx.cs" 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>
图9:登录页面包含2个TextBoxes,一个CheckBox,一个Button和一个Label
最后为LoginButton的Click事件创建一个事件处理器,只需在设计模式里双击该按钮即可.
判断提供的登录信息是否有效
现在我们需要在按钮的Click事件里执行第二项任务——判断用户提交的credentials是否有效.为此我们需要有一个user store来存储所有用户的credential.这样我们就可以判定用户提交的credential与任何已知的credential是否匹配.
在ASP.NET 2.0之前,开发人员需要自己负责user stores以及编写代码将用户提供的credentials与存储的credentials进行比较.绝大多数的开发者将user store选为一个数据库,创建一个名为Users的数据表,表的列设计为UserName, Password, Email, LastLoginDate等.一个用户帐户对应一条记录.对用户提供的credentials验证需要调用对数据库的查询,看是否有某条记录的username与用户提交的匹配,如果username匹配再看password是否匹配.
在ASP.NET 2.0里,我们只要使用众多Membership providers中的一个来管理user store,在本系列文章我们使用SqlMembershipProvider,它使用一个SQL Server数据库来作为user store.当使用SqlMembershipProvider的时候我们需要贯彻一个定义好了的数据库构架,该构架包含tables, views,stored procedures.我们在后面的文章《Creating the Membership Schema in SQL Server》里看如何来执行该构架.设置好Membership provider后,要验证用户的credentials只需要调用Membership类的ValidateUser(username, password)方法,该方法返回一个Boolean值,以指明username 和 password是否有效.
用不着花时间设计自定义的Users数据库表,我们在登录页面硬编码验证用户提供的credentials,在LoginButton的Click事件处理器里,添加如下的代码:
protected void LoginButton_Click(object sender, EventArgs e)
{ // Three valid username/password pairs: Scott/password, Jisun/password, and Sam/password.
string[] users = { "Scott", "Jisun", "Sam" };
string[] passwords = { "password", "password", "password" };
for (int i = 0; i < users.Length; i++)
{ bool validUsername = (string.Compare(UserName.Text, users[i], true) == 0);
bool validPassword = (string.Compare(Password.Text, passwords[i], false) == 0);
if (validUsername && validPassword) {
// TODO: Log in the user...
// TODO: Redirect them to the appropriate page }
}
// If we reach here, the user's credentials were invalid
InvalidCredentialsMessage.Visible = true; }
如你所见,有3个有效的用户帐户Scott, Jisun,和Sam,它们的密码都是(“password”). 代码对users和passwords数组进行循环处理,以找出一个有效的用户名和密码.如果都有效我们就需要将用户导航到恰当的页面,如果提供的credentials无效,则将名为InvalidCredentialsMessage的Label显示出来.
当用户输入一个有效的credentials时,我前面提到过要将他们返回到一个“恰当的页面”.那么什么是恰当的页面呢?我们知道,当用户访问一个未被授权的页面时,Forms Authentication自动将其导航到登陆页面,登陆成功后,就在查询字符串里的ReturnUrl参数里包含该请求的URL.那就是说,如果用户试图访问ProtectedPage.aspx页面,但其又未被授权,那么Forms Authentication就会将他导航到
Login.aspx?ReturnUrl=ProtectedPage.aspx
登陆成功后,用户就会被直接返回到ProtectedPage.aspx页面.当然用户可能自己就想访问登陆页面,在这种情况下,登陆后,应该将他们返回到根目录下的Default.aspx页面.
“登陆”用户
如果用户提供的credentials有效,那么我们就应该创建一个表单认证票据以便于用户“登陆”站点.System.Web.Security命名空间的FormsAuthentication类提供了很多通过forms authentication系统登陆和注销的函数.在此我们对3个方法感兴趣:
.GetAuthCookie(username, persistCookie)——为提供的username创建一个表单认证票据,接下来该方法创建并返回一个HttpCookie对象,该对象包含了票据的内容.如果persistCookie为true,则创建一个"持久性"的cookie.
.SetAuthCookie(username, persistCookie)——调用GetAuthCookie(username, persistCookie) 方法来生成表单认证票据.该方法将
GetAuthCookie()方法返回的cookie添加到Cookies collection里(我们假定使用的是基于cookie的forms authentication.不然,该方法将调用一个内部的类来处理无Cookie的票据逻辑)
.RedirectFromLoginPage(username, persistCookie)——该方法调用SetAuthCookie(username, persistCookie),然后将用户再导航到恰当的页面.
如果在cookie清除出Cookies collection之前,你需要对表单认证票据进行修改的话,使用GetAuthCookie方法是很方便的.如果你想创建一个表单认证票据并添加到Cookies collection,但又不想将用户导航到恰当的页面时,SetAuthCookie方法是很有用的.
由于我们希望用户“登陆”并导航到恰当的页面,因此我们使用RedirectFromLoginPage()方法.
在LoginButton按钮的Click事件处理器里,删除那2行TODO注释,添加如下的代码:
FormsAuthentication.RedirectFromLoginPage(UserName.Text, RememberMe.Checked);
创建票据时,我们将ID为UserName的那个TextBox的Text属性作为票据的username参数;将那个“RememberMe”的CheckBox的选中状态作为persistCookie参数.
来测试登陆页面。首先输入一个无效的credentials,比如用户名为“Nope”,密码为“wrong”.点击Login按钮后,将产生一个页面回传,那个ID为InvalidCredentialsMessage的Label控件就显示出来了.
图10:当输入无效的Credentials时,ID为InvalidCredentialsMessage的Label就显示出来了
接下来,输入一个有效的credentials并点Login按钮.此时产生一个页面回传,并创建一个票据,并且自动的将你导航到Default.aspx页面.此时,你已经“登陆”了,只是没有视觉上的提示而已.在第四步,我们将看到如何通过编程判断用户是否已经“登陆”,以及如何对用户进行甄别.
第五步将探讨注销用户的技术
Securing the Login Page
当用户输入credentials并提交时,该credentials——包含了用户密码——将以纯文本的形式通过英特网传递给服务器。这就意味着用户名和密码有可能被黑客截获.为避免这种事情的发生,有必要使用Secure Socket Layers (SSL)来加密处理.这将对credentials(包括正规页面的HTML标记)进行加密.
除非你的站点包含敏感信息,你只需要对登陆页面和那些一纯文本的形式传输密码的页面使用SSL。你不必担心票据的安全性,默认情况下都是经过加密处理了的.关于票据安全性的讨论见后面的文章.
注意:
很多的金融和医学网站对认证用户可以访问的页面都采用了SSL.如果你正是构建这样的网站,你可以对forms authentication system进行配置,这样票据就只能通过安全的通道进行传输.我们将在下一章《Forms Authentication Configuration and Advanced Topics》里探讨各种不同的forms authentication配置选项.
第四步:Detecting Authenticated Visitors and Determining Their Identity
此时,我们已经启用了forms authentication,并创建了一个基本的登陆页面,但我们还没有考察如何判断用户是通过认证的用户还是匿名用户.在某些情况下我们希望根据访问者是匿名的还是通过认证的来展示不同的数据或信息.此外,我们还常常需要知道认证用户的身份(identity)
让我们对现有的Default.aspx页面进行扩充,以演示这些技术.在Default.aspx页面里添加2个 Panel控件,一个ID为AuthenticatedMessagePanel,另一个为AnonymousMessagePanel.在第一个里面添加一个名为WelcomeBackMessage的Label控件 ,在第二个里面添加一个HyperLink控件,设其Text属性为“Log In”,NavigateUrl属性为“~/Login.aspx”.此时,Default.aspx页面的声明代码看起来应该和下面的差不多:
<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" 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面向匿名用户.为此,我们需要根据用户是否登陆用户来设置这2个Panels的Visible属性.
Request.IsAuthenticated属性返回一个布尔值,以指出请求是否进过了认证.在Page_Load事件处理器里键入如下的代码:
protected void Page_Load(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
WelcomeBackMessage.Text = "Welcome back!";
AuthenticatedMessagePanel.Visible = true;
AnonymousMessagePanel.Visible = false;
}
else
{
AuthenticatedMessagePanel.Visible = false;
AnonymousMessagePanel.Visible = true;
}
}
有了这些代码,在浏览器里登陆Default.aspx页面.假定你还没有登陆,你将会看到一个到登陆页面的链接(见图11),点该链接,登陆该站点.如我们在第三步所说,输入你的credentials后你将返回到Default.aspx页面,但这次页面显示的是“Welcome back!”消息(见图12).
图11:当匿名访问时,显示一个登陆链接
图12:对认证用户将显示一个“Welcome back!”的消息
我们可以通过HttpContext object的User属性来判断当前登陆用户的身份.HttpContext object对象描述了当前请求的信息,当然它也可以表示这些常见的ASP.NET objects:Response,Request,以及Session等.该User属性描述了当前HTTP request的security context,并且执行IPrincipal接口.
User属性是由FormsAuthenticationModule来设置的.具体来说,当FormsAuthenticationModule在请求里发现了一个票据,它就创建一个新的GenericPrincipal object对象并赋值给该User属性.
Principal objects对象(比如GenericPrincipal)提供了用户身份(identity)以及所属角色的信息。IPrincipal接口定义了2个成员:
.IPrincipal——返回一个布尔值的方法,指出该principal是否属于指定的角色.
.Identity——该属性返回一个执行IIdentity接口的对象.而IIdentity接口定义了3个属性:AuthenticationType, IsAuthenticated,以及Name.
我们可以通过下面的代码判断当前用户的name:
string currentUsersName = User.Identity.Name;
当使用forms authentication时候,将会为GenericPrincipal的Identity属性创建一个FormsIdentity object对象.该FormsIdentity类的AuthenticationType属性总是返回字符串“Forms” ,IsAuthenticated属性总是返回true,而Name属性返回的就是创建票据时指定的username;除了这3个属性外,FormsIdentity还有一个Ticket属性,通过该属性我们可以访问潜在的票据 .该Ticket属性返回的是一个FormsAuthenticationTicket类型的对象.该类型有诸如Expiration, IsPersistent, IssueDate, Name等的属性.
有一点要注意,对FormsAuthentication.GetAuthCookie(username,persistCookie),
FormsAuthentication.SetAuthCookie(username, persistCookie),
以及FormsAuthentication.RedirectFromLoginPage(username, persistCookie)这3个方法里指定的username参数,User.Identity.Name返回的都是一样的值.此外,这些方法创建的票据是可以获取的,方法是将User.Identity转换为一个FormsIdentity object对象,再访问该对象的Ticket属性即可,比如:
FormsIdentity ident = User.Identity as FormsIdentity;
FormsAuthenticationTicket authTicket = ident.Ticket;
让我们在Default.aspx页面里提供一个更具个性化的消息.更改Page_Load事件处理器,将WelcomeBackMessage Label的Text属性设置为“Welcome back, username!”:
WelcomeBackMessage.Text = "Welcome back, " + User.Identity.Name + "!";
如图13,显示的是更改后的效果(当以Scott的名义登陆)
图13:
使用LoginView 和 LoginName控件
接下来,在LoginContent ContentPlaceHolder里的LoginView控件下面再添加一个LoginStatus控件,设置其LogoutAction属性为Redirect;LogoutPageUrl属性为“~/Logout.aspx”,如下:
<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”LinkButton.添加了LoginStatus控件后,AnonymousTemplate模板里的那个 “Log In”链接就显得多余了,因此删除它.
图18显示的是以Jisun的名义访问Default.aspx页面的情形.我们注意到左边显示一条消息Welcome back, Jisun”,以及一个log out的链接.点击该log out LinkButton将导致一个页面回传,将Jisun从系统里注销,并将其导航到Logout.aspx页面.如图19所示,当Jisun抵达Logout.aspx页面时,她已经因被注销而变成匿名用户了.因此,左边的消息变成“Welcome, stranger”,以及一个到登录页面的链接.
图18
图19
注意:
我建议你对Logout.aspx进行一些处理(就像我们在第四步那样处理的一样).原因是在“Hello, stranger”下面的那个LoginStatus控件将呈现一个“Login” LinkButton,当一个注销了的用户点击该“Login”链接并成功登录后,LoginStatus控件将通过ReturnUrl查询字符串将用户又重新导航回Logout.aspx页面,这样将使用户感到莫名其妙.
结语:
在本文我们一开始考察了forms authentication流程,然后在一个ASP.NET应用程序里执行forms authentication.我们知道Forms authentication是由FormsAuthenticationModule来驱动的,它主要有2个职责:一个是通过表单认证票据对用户身份进行甄别,另一个是将未授权的用户导航到登录页面.
.NET Framework的FormsAuthentication类包含了很多创建、检查、销毁表单认证票据的方法.Request.IsAuthenticated属性和User对象都支持编程来判断请求是否是通过了认证,以及关于用户身份的信息.同时,LoginView, LoginStatus以及LoginName控件使开发人员可以快速的、不手写代码的方式来执行很多与登录相关的任务.我们将在以后的文章里更深入的探讨其它一些与登录相关的Web控件.
本文我们对forms authentication做了一个粗略的概述.我们没有探讨其它相关的配置选项,没有探讨无cookie的表单认证票据是如何工作的,以及ASP.NET是如何对表单认证票据的内容进行保护的.这些内容我们将在接下来的文章里探讨.
祝编程愉快!
作者简介:
Scott Mitchell,著有七本ASP/ASP.NET方面的书,是4GuysFromRolla.com的创始人,自1998年以来一直应用 微软Web技术。Scott是个独立的技术咨询顾问,培训师,作家,最近完成了将由Sams出版社出版的新作,24小时内精通ASP.NET 2.0。他的联系电邮为mitchell@4guysfromrolla.com,也可以通过他的博客http://ScottOnWriting.NET与他联系。