根据Membership用户存储验证用户凭据

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

在本教程中,我们将探讨怎样通过编码和使用登录控件这两种方式,根据Membership 用户存储来验证用户的凭据。我们还将了解怎样定制登录控件的外观和行为。

<<前一篇教程下一篇教程>>

简介

上一篇教程 中,我们了解了怎样在Membership 框架中创建一个新的用户帐户。我们首先了解了怎样使用Membership 类的CreateUser 方法通过编码创建用户帐户,然后探讨了怎样使用CreateUserWizard Web 控件来创建用户帐户。然而,当前的登录页面对照含有用户名和密码对的固定列表来验证用户提供的凭据。我们需要更改登录页面的逻辑,使其根据Membership 框架的用户存储来验证凭据。

与创建用户帐户非常类似的是,可通过编码和声明两种方式来验证凭据。Membership API 中有一个方法,用这个方法编码,可以实现根据用户存储来验证用户凭据。另外,ASP.NET 随带有Web 登录控件,该控件展示一个用户界面,界面上有一个用户名文本框、一个密码文本框,以及一个登录按钮。

在本教程中,我们将探讨怎样通过编码和使用登录控件这两种方式,根据Membership 用户存储来验证用户的凭据。我们还将了解怎样定制登录控件的外观和行为。 让我们开始吧!

步骤1 : 根据Membership 用户存储验证凭据

对于使用表单身份验证的 web 网站来说,用户通过访问登录页面并输入自己的凭据来登录网站。系统会将用户输入的凭据与用户存储中的内容相对比。如果凭据有效,则会授予该用户一个表单身份验证票证,该票证是一个安全令牌,用以指示该访问者的身份标识和真实性。

要根据 Membership 框架来验证用户,可使用 Membership 类的 ValidateUser 方法 。ValidateUser 方法接受两个输入参数 – usernamepassword ,它返回一个布尔值来指示用户凭据是否有效。和上一篇教程中讲过的CreateUser 方法一样,ValidateUser 方法将实际的验证工作委托给已配置的Membership 提供者。

SqlMembershipProvider 通过aspnet_Membership_GetPasswordWithFormat 存储过程获取指定用户的密码,从而对用户提供的凭据进行验证。我们记得,SqlMembershipProvider 以这三种格式之一保存用户密码:明文、加密、散列(hashed) 。aspnet_Membership_GetPasswordWithFormat 存储过程以其原始格式返回密码。对于加密密码和散列密码,SqlMembershipProvider 将传入到 ValidateUser 方法中的 password 值转换为其对应的加密或散列状态,然后将其与数据库的返回值相对比。如果数据库中保存的密码与用户输入并经过格式化处理后的密码相匹配,用户凭据就是有效的。

现在我们来更改登录页面(~/Login.aspx) ,使该页面根据Membership 框架的用户存储来验证用户提供的凭据。这个登录页面是我们在《表单身份验证概述 》教程中创建的,在那里,我们创建了一个界面,该界面有两个文本框,分别用于用户名和密码,一个“Remember Me” 复选框,以及一个Login 按钮(参见图1 )。该页面的代码根据一个含有用户名和密码对(Scott/password 、Jisun/password 和Sam/password )的固定列表来验证用户输入的凭据。在《表单身份验证配置与高级主题 》教程中,我们更改了该登录页面的代码,使其将额外信息保存于表单身份验证票证的UserData 属性。

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

登录页面的用户界面可以不作改动,但需要更换Login 按钮的 Click 事件处理程序的代码,使新的代码根据Membership 框架的用户存储来验证用户。更改该事件处理程序后,其代码如下:

protected void LoginButton_Click(object sender, EventArgs e)
{
     // Validate the user against the Membership framework user store
     if (Membership.ValidateUser(UserName.Text, Password.Text))
     {
          // Log the user into the site
          FormsAuthentication.RedirectFromLoginPage(UserName.Text, RememberMe.Checked);
     }
     // If we reach here, the user's credentials were invalid
     InvalidCredentialsMessage.Visible = true;
}

该代码相当简单。代码首先调用了Membership.ValidateUser 方法,将所提供的用户名和密码传入该方法。. 若该方法返回为真,则通过FormsAuthentication 类的RedirectFromLoginPage 方法使用户登录进网站。(如《表单身份验证概述 》教程所述,FormsAuthentication.RedirectFromLoginPage 创建表单身份验证票证,然后将用户重定向到恰当的页面。)然而,若票证无效,会显示InvalidCredentialsMessage 标签,告知用户其输入的用户名或密码不正确。

这样就可以了!

为了测试该登录页面,看其是否如预期一样工作,可以试着以上一篇教程中创建的一个用户帐户登录。如果尚未创建帐户,可通过~/Membership/CreatingUserAccounts.aspx 页面创建一个再进行测试。

注意: 当用户输入自己的凭据并提交登录页面表单时,包括密码在内的凭据信息以明文的形式通过互联网传给Web 服务器。这意味着,任何正在窃听网络流量的黑客都可以看到该用户名和密码。为了阻止该问题,使用安全套接字层 (SSL) 对网络数据流进行加密是必要的。这将确保凭据(以及整个页面的HTML 标记)从离开浏览器那一刻起就已加密,直到它们为Web 服务器所接收。

Membership 框架怎样处理无效登录尝试

当访问者访问登录页面并提交其凭据时,浏览器会向登录页面发出一个HTTP 请求。若凭据有效,HTTP 响应会将身份验证票证放入一个 cookie 中。因此,企图闯入您的网站的黑客可能会编制一个程序,该程序以穷尽的方式向登录页面发送许多带有有效用户名和猜测的密码的HTTP 请求。如果密码猜对了,登录页面会返回带有身份验证票证的cookie ,此时,程序知道它碰上了一对有效的用户名/ 密码。通过穷举式暴力破解,这种程序偶尔可能会发现一个用户密码,特别是当密码安全性比较弱时。

为了防止这类暴力破解攻击,当一定时间内不成功的登录尝试次数达到一定数量时,Membership 框架会锁定该用户。我们可以通过下面Membership 提供者配置的两个设置项来配置所需参数:

  • maxInvalidPasswordAttempts – 指定在一个时间周期内允许用户进行的无效密码尝试的次数,超过该次数会锁定相应帐户。默认值为5 。
  • passwordAttemptWindow – 以分钟为单位指定一个时间周期,如果在该时间内达到指定的无效登录尝试次数,就会锁定帐户。默认值为10 。

用户被锁定后是无法登录的,除非管理员对其帐户进行了解锁。当用户处于锁定状态时,ValidateUser 方法总是返回假值,即使提供了有效的凭据也是如此。尽管此行为使得黑客通过暴力破解方法闯入网站的可能性降低,它却可能导致这样的结果:一个有效用户,只因其忘记了密码、不经意间按下了Caps Lock 键,或者屡次输入错误而被锁定。

遗憾的是,没有一个内置的工具可以对用户帐户进行解锁。要解锁一个帐户,可以直接更改数据库– 更改 aspnet_Membership 表中相应用户帐户的 IsLockedOut 字段,也可以创建这样一个基于Web 的界面,该界面列出被锁定的帐户,每个锁定帐户有一个用于解锁的选项。我们将在后面的一个教程中探讨怎样创建管理界面来完成常见的与用户帐户和角色有关的任务。

注意:ValidateUser 方法的一个缺点是,当用户提供的凭据无效时,该方法不提供任何的原因解释。凭据无效的可能原因有:用户存储中没有匹配的用户名/ 密码对、用户尚未得到批准、用户被锁定。在步骤4 中我们将看到,怎样在登录尝试失败时向用户展示更为详尽的消息。

步骤2 :通过Web 登录控件收集凭据

Web 登录控件 展示了一个默认的用户界面,该界面非常类似于我们在《表单身份验证概述 》教程中创建的那个界面。使用Login 控件,我们就不用再创建收集访问者凭据的界面了。而且,Login 控件在提交的凭据有效时会自动使用户登录进来,因而我们不必再编写任何的代码。

现在我们来更新 Login.aspx ,用一个Login 控件来取代手工创建的界面和代码。首先删除Login.aspx 中现有的标记和代码。可以直截了当地删除,也可以将其注释掉。要注释掉声明标记,用<%-- 和 --%> 定界符将其括起。可以手工输入这些定界符,也可以这样做:选择要注释掉的文本,单击工具栏中的“Comment out the selected lines” 图标,如图 2 所示。同样,也可以使用 “Comment out the selected lines” 图标在 code-behind 类中注释掉所选代码。

图2 :注释掉 Login.aspx 中的现有声明标记和源代码(单击此处查看实际大小的图像

注意 : 使用Visual Studio 2005 来查看声明标记时没有 “Comment out the selected lines” 图标。如果当前使用的不是 Visual Studio 2008 ,则需要手工添加 <%-- and --%> 定界符。

接下来,从工具箱中将一个 Login 控件拖放到页面中,将该控件的ID 属性设置为 myLogin 。此时,屏幕外观应类似于图 3 。注意,Login 控件的默认界面包含两个文本框— 用户名文本框和密码文本框、一个“Remember me next time” 复选框、一个“Log In” 按钮。这两个文本框各自还有一个RequiredFieldValidator 控件。

图3 : 向页面添加 Login 控件 (单击此处查看实际大小的图像

如此,我们就完成了! 在单击Login 控件的 “Log In” 按钮时,系统会发生一次回传,Login 控件会调用 Membership.ValidateUser 方法,将输入的用户名和密码传给该方法。如果凭据无效,Login 控件会显示一条消息来说明这点。而如果凭据有效,Login 控件会创建表单身份验证票证,然后将用户重定向到恰当的页面。

当用户登录成功时,Login 控件根据下面四个因素来决定将用户重定向到哪个“恰当的页面”:

  • Login 控件是否在表单身份验证配置的 loginUrl 设置(该设置的默认值为 Login.aspx )定义的登录页面上
  • 是否存在 ReturnUrl 查询字符串参数
  • Login 控件的 DestinationUrl 属性的值
  • 表单身份验证配置设置中指定的 defaultUrl 值,该设置的默认值是 Default.aspx

图 4 描绘了Login 控件怎样用这四个参数来决定哪个是“恰当的页面”

图4 : 重定向到恰当的页面[MU1]  (单击此处查看实际大小的图像

我们花点时间来测试一下这个 Login 控件:通过浏览器访问站点,以 Membership 框架中一个现有用户的身份进行登录。

Login 控件展示的界面是高度可配置的。该界面有许多可影响其外观的属性;另外,还可将Login 控件转化为模板,以便可以精确地控制各用户界面元素的布局。本步骤剩余部分探讨怎样定制该控件的外观和布局。

定制Login 控件的外观

在其默认的属性设置下,Login 控件展示的用户界面上有一个标题 (Log In) 、用于输入用户名和密码的两个文本框控件和相应的标签控件、一个“Remember me next time” 复选框,以及一个 “Log In” 按钮。这些元素的外观都是可以配置的,可通过Login 控件的众多属性来配置。另外,还可添加其它的用户界面元素,比如一个链接,通过该链接可转到创建新的用户帐户的页面,我们通过设置一两个属性就可添加上。

现在我们花点时间来修饰一下 Login 控件的外观。既然Login.aspx 页面在其顶部已经有了“Login” 文本,Login 控件的标题就是多余的了。因此,我们清除TitleText 属性 值以便去掉 Login 控件的标题。

两个文本框控件左边的 "User Name:” 和 “Password:” 标签分别可通过 UserNameLabelText 属性PasswordLabelText 属性 来定制。我们将 “User Name:” 标签内容改为“Username:” 。标签和文本框的样式都是可配置的,可分别通过LabelStyle 属性TextBoxStyle 属性 来配置。

对于 “Remember me next time” 复选框,可通过Login 控件的RememberMeText 属性 来设置其 Text 属性,通过RememberMeSet 属性 设置其默认的选中状态(默认为False )。这里我们将 RememberMeSet 属性设置为 True ,这样在默认情况下 “Remember me next time” 复选框是选中的。

Login 控件提供了两个属性来调整其用户界面中控件的布局。一个是TextLayout 属性 ,该属性指示 “Username:” 和“Password:” 标签是显示于对应文本框的左侧还是上方(默认是左侧)。另一个是Orientation 属性 ,该属性指示用户名和密码这两个输入框是垂直放置的(一个在另一个之上)还是水平放置的。这里我让这两个属性保持其默认设置,但鼓励您尝试将这两个属性设置为非默认值以查看其最终效果。

注意: 在下一节,“配置Login 控件的布局”中,我们会看到,可以使用模板来精确地定义Login [微软用户2]  控件用户界面元素的布局。

在Login 控件属性设置的最后,我们将CreateUserText 属性CreateUserUrl 属性 分别设置为“Not registered yet?Create an account!” 和“~/Membership/CreatingUserAccounts.aspx” 。这会给页面添加一个超级链接,该超链指向我们在上一篇教程 中创建的页面。Login 控件的HelpPageText 属性HelpPageUrl 属性 以及PasswordRecoveryText 属性PasswordRecoveryUrl 属性 的作用类似于此,它们分别提供可转到帮助页面和密码恢复页面的链接。

在完成这些属性更改后,Login 控件的声明标记和外观应类似于图 5 :

图5 :Login 控件的属性值规定了该控件的外观(单击此处查看实际大小的图像

配置Login 控件的布局

Login Web 控件的默认用户界面以一个 HTML <table> 中的代码来描绘其界面。但如果我们需要更精确地控制输出画面该怎么办呢?也许我们想将<table> 替换为一系列的 <div> 标签。或者,如果我们的应用程序需要更多的身份验证凭据,又该怎么办?比如,许多金融网站不但要求用户提供用户名和密码,还要求用户提供个人身份识别号码(PIN) 或其它的身份识别信息。不管是什么原因,我们有可能要将Login 控件转换为一个模板,在该模板中我们可以直接定义界面的声明标记。

为了更改 Login 控件,使其收集更多的凭据,我们要做两件事情:

  1. 更改 Login 控件的界面:将收集其它凭据的 Web 控件放入其中。
  2. 重写 Login 控件的内部身份验证逻辑:使得只有当用户名与密码有效并且其它凭据也有效时才会对用户进行验证。

为了完成第一项任务,我们要将Login 控件转换为模板,然后在其中添加必要的Web 控件。至于第二项任务,可以为 Login 控件的Authenticate 事件 创建一个事件处理程序来取代 Login 控件的身份验证逻辑。

现在我们来更新 Login 控件,使其提示用户输入其用户名、密码以及Email 地址,并且只有在用户提供的 Email 地址与存档的 Email 地址相符时才对用户进行身份验证。首先,我们要将Login 控件的界面转换为模板。在 Login 控件的智能标记中选择 “Convert to template” 选项。

图6 :将 Login 控件转换为模板(单击此处查看实际大小的图像

注意 : 要将Login 控件回复到其模板之前的形式,单击该控件的智能标记中的Reset 链接。

将 Login 控件转换为模板,会使该控件对用户界面进行定义的HTML 元素和 Web 控件的声明标记中增加一个 LayoutTemplate 。如图 7 所示,控件转换为模板后,在 Properties 窗口中有许多属性消失了,比如 TitleText 、CreateUserUrl 等属性,因为使用模板时会忽略这些属性值。

图7 :Login 控件转换为模板后,Properties 窗口中的属性变少了(单击此处查看实际大小的图像

可以根据需要来修改 LayoutTemplate 中的HTML 标记。同样,也可在模板中随意添加任何新的Web 控件。然而,在模板中保留 Login 控件的核心 Web 控件及其 ID 赋值是很重要的。特别是,不要删除也不要重命名UserName 和 Password 文本框、RememberMe 复选框、LoginButton 按钮、FailureText 标签、RequiredFieldValidator 控件。

为了收集访问者的 Email 地址,我们要在模板中添加一个文本框。在包含Password 文本框的 table 行 (<tr>) 和包含 “Remember me next time” 复选框的 table 行之间添加以下声明标记:

<tr>
     <td align="right">
          <asp:Label ID="EmailLabel" runat="server" AssociatedControlID="Email">Email:</asp:Label>
     </td>
     <td>
          <asp:TextBox ID="Email" runat="server"></asp:TextBox>
          <asp:RequiredFieldValidator ID="EmailRequired" runat="server" 
               ControlToValidate="Email" ErrorMessage="Email is required." 
               ToolTip="Email is required." ValidationGroup="myLogin">*</asp:RequiredFieldValidator>
     </td>
</tr>

添加了 Email 文本框后,通过浏览器访问该页面。现在,Login 控件的用户界面有了第三个文本框,如图8 所示。

图8 :Login 控件现在有一个可输入用户 Email 地址的文本框(单击此处查看实际大小的图像

此时,Login 控件仍然使用Membership.ValidateUser 方法来验证用户提供的凭据。相应地,输入到Email 文本框中的值与用户是否可以登录是毫无联系的。在步骤3 中,我们会考察怎样重写 Login 控件的身份验证逻辑,使得只有在用户名和密码有效,且所提供的Email 地址与存档的 Email 地址相符时,这些凭据才会被视为有效。

步骤3 : 修改Login 控件的身份验证逻辑

在访问者提供凭据并单击 “Log In” 按钮后,会发生一次回传,Login 控件进行其身份验证流程。该流程从触发LoggingIn 事件 开始。该事件关联的任何事件处理程序都可以通过将e.Cancel 属性设置为 true 来取消这次登录操作。

如果该登录操作未被取消,身份验证流程会继续进行,这时会触发Authenticate 事件 。如果 Authenticate 事件有一个事件处理程序,会由该事件处理程序来确定用户提供的凭据是否有效。如果没有指定事件处理程序,Login 控件会使用 Membership.ValidateUser 方法来确定凭据的有效性。

如果用户提供的凭据有效,系统会创建一个表单身份验证票证,触发LoggedIn 事件 ,然后将用户重定向到恰当的页面。然而,如果凭据被视为无效,则会触发LoginError 事件 ,于是系统显示一条消息,告知用户其凭据是无效的。默认情况下,登录失败时,Login 控件只是将 FailureText 标签控件的 Text 属性设置为一条失败消息:“Your login attempt was not successful.Please try again” 。但是,如果 Login 控件的 FailureAction 属性 设置为了 RedirectToLoginPage ,则Login 控件会向登录页面发出一个 Response.Redirect ,其后面带有查询字符串参数 loginfailure=1 (这使得Login 控件显示失败消息)。

图 9 给出了身份验证流程的流程图。

图9 :Login 控件的身份验证流程(单击此处查看实际大小的图像

注意 : 如果对何时使用FailureAction 的 RedirectToLoginPage[微软用户1]  选项感到迷惑,可想想下面的情形。受到匿名用户的访问时,我们当前的Site.master 母版页会立刻在左侧栏中显示这样的文本:“Hello, stranger” 。假设我们要用一个Login 控件取代该文本。这样将会允许匿名用户通过站点上的任一页面来登录,而不是要求他们直接访问登录页面。然而,如果用户通过母版页提供的这个Login 控件登录失败,那么将用户重定向到登录页面(Login.aspx) 就是有意义的了。因为该页面很可能含有附加的指示说明、链接,以及其它帮助,例如,创建新帐户的链接、获取遗忘的密码的链接,而这些不会添加到母版页中。

创建Authenticate 事件处理程序

为了插入定制的身份验证逻辑,我们需要为Login 控件的 Authenticate 事件创建一个事件处理程序。为 Authenticate 事件创建事件处理程序会生成下面的事件处理程序定义:

protected void myLogin_Authenticate(object sender, AuthenticateEventArgs e)
{
}

我们可以看到,传入 Authenticate 事件处理程序的第二个输入参数是一个AuthenticateEventArgs 类型的对象。AuthenticateEventArgs 类包含一个名为Authenticated 的 Boolean 属性,用于指示用户提供的凭据是否有效。那么,我们的任务是,在这里编写代码来确定用户提供的凭据是否有效,然后相应地设置e. Authenticated[微软用户1]  属性。

确定并验证用户提供的凭据

可以用 Login 控件的 UserName 属性Password 属性 来确定用户输入的用户名和密码凭据。为了确定输入到任何其它Web 控件中的值(例如我们在上一步中添加的Email 文本框),可以使用LoginControlID.FindControl("controlID") 来编码引用这个 Web 控件,其中,controlID 为模板中该 Web 控件的ID 属性。例如,要引用 Email 文本框控件,使用下面的代码:

TextBox EmailTextBox = myLogin.FindControl("Email") as TextBox;

在验证用户凭据时,我们要做以下两件事情:

  1. 确保所提供的用户名和密码是有效的
  2. 确保尝试登录的用户输入的 Email 地址与存档的 Email 地址相符

为了完成第一项检查,我们可以只使用Membership.ValidateUser 方法,该方法在步骤 1 中讲过。对于第二项检查,我们需要确定用户的Email 地址,以便我们可以将其与用户输入到地址文本框控件中的地址相比较。要获取某特定用户的信息,可使用Membership 类的 GetUser 方法

GetUser 方法有许多重载。如果使用该方法时不向其传递任何的参数,该方法会返回当前已登录用户的信息。要获取特定用户的信息,在调用GetUser 方法时要将用户名传递给它。无论传还是不传参数,GetUser 都会返回一个MembershipUser 对象 ,这个对象含有诸如 UserName 、Email 、IsApproved 、IsOnline 等属性。

下面的代码实现了这两项检查。如果这两项检查都通过了,则e. Authenticated[微软用户2]  会设置为 true ,否则设置为 false 。

protected void myLogin_Authenticate(object sender, AuthenticateEventArgs e)
{
     // Get the email address entered
     TextBox EmailTextBox = myLogin.FindControl("Email") as TextBox;
     string email = EmailTextBox.Text.Trim();
     // Verify that the username/password pair is valid
     if (Membership.ValidateUser(myLogin.UserName, myLogin.Password))
     {
          // Username/password are valid, check email
          MembershipUser usrInfo = Membership.GetUser(myLogin.UserName);
          if (usrInfo != null && string.Compare(usrInfo.Email, email, true) == 0)
          {
               // Email matches, the credentials are valid
               e.Authenticated = true;
          }
          else
          {
               // Email address is invalid...
               e.Authenticated = false;
          }
     }
     else
     {
          // Username/password are not valid...
          e.Authenticated = false;
     }
}

完成上述代码后,试着以一个有效用户的身份登录,输入正确的用户名、密码和Email 地址。之后再试一次,但这次特意使用一个不正确的Email 地址(参见图 10 )。最后,进行第三次尝试,这次使用一个不存在的用户名。第一次会成功地登录到网站中,而后面两次会看到Login 控件给出的无效凭据消息。

图10 :用户 Tito 提供了不正确的 Email 地址,因而无法登录(单击此处查看实际大小的图像

注意 : 在步骤1 的“Membership 框架怎样处理无效登录尝试”一节中我们讲过,系统在调用Membership.ValidateUser 方法并向其传递无效凭据时,会记录无效登录尝试,如果用户在一个指定的时间窗内进行的无效登录尝试的次数超过一定的阈值,就会锁定该用户。由于我们定制的身份验证逻辑调用的是ValidateUser 方法,如果用户输入了有效的用户名但输入的密码不正确,无效登录尝试计数器会递增,而如果用户名和密码有效但Email 地址不正确,该计数器不会递增。大多数情况下该行为是合适的,因为不太可能出现这种情况:黑客知道用户名和密码,但需要使用暴力破解技术来确定用户的Email 地址。

步骤4 : 改进Login 控件的无效凭据消息

在用户试图以无效凭据登录时,Login 控件会显示一条消息来告知登录尝试不成功。具体来说,该控件显示其FailureText 属性 指定的消息,而这个属性的默认值为“Your login attempt was not successful.Please try again” 。

我们知道,用户凭据无效的原因有许多:

  • 用户名可能不存在
  • 用户名存在,但密码无效
  • 用户名和密码都有效,但用户尚未被批准
  • 用户名和密码都有效,但用户被锁定(很可能是由于指定时间帧内的无效登录尝试的次数超过阈值)

而使用定制的身份验证逻辑,还可能有其它的原因。例如,对于我们在步骤3 中编写的代码,也可能是用户名和密码有效但Email 地址不正确。

只要凭据无效,不管是何原因引起的,Login 控件都会显示相同的错误消息。这种反馈信息不足的情况会使未得到批准或被锁定的用户搞不清楚实际的情况。而我们只需做一点工作,就可使Login 控件显示更为确切的消息。

每当用户试图以无效凭据登录时,Login 控件会触发其LoginError 事件。下面我们为此事件创建一个事件处理程序并在其中添加以下代码:

protected void myLogin_LoginError(object sender, EventArgs e)
{
     // Determine why the user could not login...
     myLogin.FailureText = "Your login attempt was not successful. Please try again.";
     // Does there exist a User account for this user?
     MembershipUser usrInfo = Membership.GetUser(myLogin.UserName);
     if (usrInfo != null)
     {
          // Is this user locked out?
          if (usrInfo.IsLockedOut)
          {
               myLogin.FailureText = "Your account has been locked out because of too many invalid login attempts.
     Please contact the administrator to have your account unlocked.";
          }
          else if (!usrInfo.IsApproved)
          {
               myLogin.FailureText = "Your account has not yet been approved.
     You cannot login until an administrator has approved your account.";
          }
     }
}

上面的代码开始时将 Login 控件的FailureText 属性设置为默认值 (“Your login attempt was not successful.Please try again”) 。然后检查所提供的用户名是否对应一个现有的用户帐户。如果有对应的帐户,则查询所得到的MembershipUser 对象的 IsLockedOut 和 IsApproved 属性以确定该帐户是否被锁定、是否尚未得到批准。如果被锁定或未经过批准,就将FailureText 属性更改为相应的值。

为了测试该代码,试着以一个现有用户的身份登录,但特意使用一个不正确的密码。在10 分钟时间帧内这样连续登录五次,就会使相应帐户锁定。如图11 所示,随后的登录尝试统统会失败,即使使用了正确的密码,但此时会显示更能说明问题的以下消息:“Your account has been locked out because of too many invalid login attempts.Please contact the administrator to have your account unlocked” 。

图11 :用户 Tito 进行的无效登录尝试过多因而已被锁定(单击此处查看实际大小的图像

小结

在本教程之前,我们的登录页面根据一个含有用户名和密码对的固定的列表来验证用户提供的凭据。在本教程中,我们更改了页面代码,使其根据Membership 框架来验证凭据。在步骤 1 中我们看到了怎样使用Membership.ValidateUser 方法编程。在步骤 2 中我们以 Login 控件取代了我们手工创建的用户界面和代码。

Login 控件展示一个标准的登录用户界面,可根据Membership 框架自动验证用户凭据。另外,当用户提供了有效凭据时,Login 控件会通过表单身份验证使用户登录进来。简而言之,只需将Login 控件拖放到页面,就可获得完整而实用的登录用户体验,并不需要添加额外的声明标记或代码。而且,Login 控件是高度可定制的,允许我们精确地控制其用户界面和身份验证逻辑。

至此,我们网站的访问者已经可以创建一个新的用户帐户并登录到网站了,但我们尚不知道怎样在用户经过身份验证后按用户来限制对网页的访问。目前,任何用户,无论是经过身份验证的用户还是匿名用户,都可以查看我们网站的任何页面。随着对我们网站页面的访问的控制,我们可能有某些页面会根据用户决定其提供的功能。在下一篇教程中,我们将考察怎样按已登录用户来对页面访问和页面内的功能进行限制。

快乐编程!

 

下一篇教程