ASP.NET 2.0 Login控件

作者:Keith Brown
相关技术:安全、ASP.NET 2.0
难度:★★☆☆☆

    [导读]在安全已经成为首要问题的今天,ASP.NET 2.0给了开发人员极大的信心,从新引入的服务器段安全控件、角色定义、密码恢复及其基于成员和角色的编程,ASP.NET 2.0让您的系统更加安全可靠。

    新的安全性功能是ASP.NET 2.0中的一项重大改进。这些功能包括管理用户帐户数据库的成员身份服务、哈希密码、管理用户角色成员身份的角色管理器,以及可以更容易实现窗体身份验证的五个新的服务器端控件。ASP.NET 2.0还提供了提供程序模型(Provider Model),使您能够完全控制Membership和Role服务以及无cookie窗体身份验证的实现。您还可以非常轻松地对用户帐户和角色进行简单的、基于Web的本地和远程管理,并且可以获得对其他非安全性相关设置的增强控制。

重推窗体身份验证

    窗体身份验证是ASP.NET 1.0中使用最广泛的功能之一,因为它封装了许多特定实现所缺少的最佳实践。例如,您知道有多少窗体身份验证实现可以保护用来存放客户端凭证的cookie的完整性吗?窗体身份验证不仅将用户名写入该cookie中,而且还添加了一个消息身份验证代码(一个根据该cookie形成的哈希值和只有Web服务器知道的秘密值)。这可以防止恶意的客户端通过提高权限或通过修改其cookie中的名称来查看其他用户的数据。

    如果留意.NET Web开发人员推出的各种新闻组和列表服务器,您将了解到人们正在一遍又一遍地实现相同的东西:用户数据库、cookie中缓存的角色、捕获用户名和密码的控件、管理用户和角色的工具。ASP.NET小组已经提供了针对几乎所有这些问题的内置解决方案。在研究ASP.NET 2.0的alpha版本时,我发现在以可管理的方式构建使用窗体身份验证的Web站点时,所使用的代码量急剧减少。

入门

    如果您有ASP.NET 2.0(可用于MSDN),那么在演练某些可以执行的实验时,您就会发现开始使用这些新功能是多么地简单!普通订阅者可以下载。

    要开始下载,您需要一个指向空目录的虚拟目录。您必须确保ASP.NET辅助进程具有读取、执行和写入该目录的权限。如果您运行的是Windows 2000或Windows XP,则需要为ASP.NET本地帐户赋予这些权限;而对于Windows Server 2003,您需要为网络服务帐户赋予这些权限。

    我将使用窗体身份验证,所以我需要通过web.config文件来启用它。如果我现在向您展示如何使用ASP.NET 1.1,那么您需要打开一个文本编辑器并开始手动键入XML。但是在ASP.NET 2.0中,我最喜欢的功能之一是交互式配置文件编辑器,它直接构建于IIS管理控制台,您可以在虚拟目录的属性表的“ASP.NET”选项卡中找到它。单击“Edit configuration”按钮以打开编辑器。


图1 配置编辑器

    图1显示了这个新编辑器。您会看到我选择了窗体身份验证而不是默认选项:Windows身份验证。在您自己的虚拟目录中进行同样的操作。当使用配置工具时,将Web应用程序的默认语言设置为C#,因为它将替您省去一些后面需要进行的输入。“Page Language Default”设置是Application选项卡上的第一个下拉选项。在应用这些更改之后,您将在目录中找到web.config文件并带有所有设置。

    您需要向Membership服务注册一些用户以便开始,因此编写的第一页是允许添加用户的页。在该测试版中提供了一个服务器控件,通过该控件,您可以使用下面三行代码来实现该页:

<form runat='server'>
    <asp:createuser runat='server'/>
</form>

<form runat='server'>
    <table>
        <tr><td>Name:</td><td><asp:textbox id='name' runat='server'/></td></tr>
        <tr><td>Email address:</td><td><asp:textbox
            id='email' runat='server'/>
        </td></tr>
        <tr><td>Password:</td><td><asp:textbox textmode='Password'
            id='pwd1' runat='server'/>
        </td></tr>
        <tr><td>Retype password:</td><td><asp:textbox textmode='Password'
            id='pwd2' runat='server'/>
        </td></tr>
    </table>
    <asp:button text='Submit' onclick='onSubmit' runat='server'/>
    <p><asp:label id='msg' runat='server'/></p>
</form>

<script runat='server'>
void onSubmit(object sender, EventArgs args)
{
    if (pwd1.Text.Equals(pwd2.Text))
    {
        MembershipCreateStatus status;
        MembershipUser newUser = Membership.CreateUser(name.Text,
            pwd1.Text, email.Text, out status);
        msg.Text = status.ToString();
    }
    else
        msg.Text = "Passwords don't match, try again.";
}
</script>

图2 adduser.aspx

    不过,由于我使用的版本是alpha,所以我必须直接使用Membership类来手工为这个特殊的窗体编写代码。而现在,只需使用图2 中所示的ASPX页就可以了,我将在本文稍后讨论Membership类。图3显示当您将浏览器指向该页时所看到的内容。继续进行试验,现在添加一些用户和密码。成功后,您的工作应该更加轻松!


图3 成员页

    完成用户添加之后,请仔细查看虚拟目录。您应该看到一个名为“DATA”新的子目录,其中包含了一个Microsoft® Access数据库。这是Membership和Role服务默认存储其数据的地方,但是稍后我会向您展示如何覆盖默认存储机制以使用SQL Server,或您自己的自定义数据储备库。现在,您可以使用ASP.NET 2.0中的安全控件了。

服务器端安全控件

    图4 列出了ASP.NET 2.0中的五个新安全控件。从LoginStatus控件开始探索是个好主意。首先创建一个包含该控件的新ASPX页。为了简单起见,调用新页面default.aspx:

<form runat='server'>
    <asp:loginstatus runat='server'/>
</form>

控件描述
LoginStatus提供一个登录或者注销的按钮,主要依赖于用户的当前状态
Login提供一个表单用于收集和校验依赖于用户数据库的登录凭证
LoginName显示一个已经登录的用户名
LoginView显示特定的内容,依赖于用户是否登录,而用户是否是一个角色的成员是可选的
PasswordRecovery提供一个找回忘记的密码需要的清单

图4 ASP.NET 2.0新的安全控件

    将浏览器指向该页面,您应该看到一个Login链接。如果您在浏览器中查看结果页面的源代码,您将看到这个超级链接指向一个名为login.aspx的页面,但您还没有编写它。这又是一个用三行代码实现的Web页,因此我们继续进行试验,现在就创建它:

<form runat='server'>
    <asp:login runat='server'/>
</form>

    如果您曾经手工实现过窗体身份验证,您就会赞赏这三行代码。过去,执行数据库查找的等同实现需要两倍数量的代码。

    现在回到您的浏览器,并单击Login链接,它将您带到如图5所示的登录页。尝试用一个无效用户名或密码登录,可以发现,系统会弹出一条适当的默认错误消息。这条消息不会给攻击者太多的信息。而一个没有经验的开发人员也决不会无意中发送回一条消息给该用户,告诉他获得了正确的用户名,请尝试猜测另一个密码!


图5 登录页

    继续进行并键入一个有效的用户名和密码,这是您先前通过adduser.aspx页输入的用户名和密码,然后您应该被重定向回default.aspx页。由于您没有为登录控件提供任何自定义操作,所以默认情况下它只通过窗体身份验证来让您登录,这意味着您的浏览器现在有了一个存放用户名的加密cookie。

    既然您已经重定向回default.aspx页面,您看到有什么不同吗?登录状态控件现在应该显示Logout而不是Login。因为窗体身份验证cookie是与请求一起发送的,所以FormsAuthenticationModule创建了一个经过身份验证的用户主体,并且将其与该请求的上下文相关联。登录状态控件会注意到这种情况,并且改变成允许您注销。尝试注销并重新登录来查看这项工作。

    现在,让我们再添加一些代码到default.aspx页面:

<h3>User Name: <%= User.Identity.Name %></h3>
<h3>User Type: <%= User.GetType() %></h3>

    刷新这个页面,会看到您用来登录的用户名。请注意,表示用户的基本对象是GenericPrincipal类型,这是FormsAuthenticationModule表示用户的方式。一旦您启动Role Manager,就会注意到这种类型变化,因为当启用时,新的RoleManagerModule就取代了由FormsAuthentication使用它自己的类型生成的用户。

    现在,让我们添加一个LoginView控件到default.aspx页面,以显示可以根据用户的登录而改变的内容。使用这个控件最简单的方法是提供两个内容块:一个用于匿名请求(在用户登录之前),另一个用于身份验证请求(在用户登录之后):

<asp:loginview runat='server'>
    <anonymoustemplate>
        <h4>If you see this, you've not yet logged in!</h4>
    </anonymoustemplate>
    <loggedintemplate>
        <h4>Welcome to my website, <asp:loginname runat='server'/>!</h4>
    </loggedintemplate>
</asp:loginview>

    当您登录或注销时,您应该看到LoginView控件中的文本发生了改变,正如我们所预料的一样。这是一个非常简单的想法,但是它确实让您的代码变得更清晰。

定义角色

    我已经制作了一个简单的页面,它允许您使用Role Manager将用户添加到角色,但是在您能使用它之前,还需要为应用程序启用Role Manager。回到配置工具,并找到“Authentication”选项卡。选中标有“Role management enabled”的复选框,然后应用这个改变。

<form runat='server'>
    <table>
        <tr><td>Role:</td><td><asp:textbox id='role' runat='server'/></td></tr>
        <tr><td>User:</td><td><asp:textbox id='user' runat='server'/></td></tr>
    </table>
    <asp:button text='Add user to role!' onclick='onSubmit' runat='server'/>
    <p><asp:label id='msg' runat='server' viewstateenabled='false'/></p>
</form>

<script runat='server'>
void onSubmit(object sender, EventArgs args)
{
    if (!Roles.RoleExists(role.Text))
    {
        Roles.CreateRole(role.Text);
        msg.Text = "Created a new role.";
    }
    Roles.AddUserToRole(user.Text, role.Text);
}
</script>

图6 addrole.aspx

    在图6 中显示了addrole.aspx页面的代码,而图7显示了窗体的外观。将这个页面放到虚拟目录中并且将浏览器指向它,这样您就可以添加了一些角色。指定一个用户名(您前面通过adduser.aspx窗体添加的用户名)和一个角色名,然后按下按纽将用户添加到角色中。代码将首先添加角色(如果它不存在的话),然后将用户添加到角色。在后台,Role Manager会在Membership服务使用的同一Microsoft Access数据库中跟踪这些角色映射,但是这实际上只是巧合。Role Manager可以将其数据存储在SQL Server或任何其他的存储中,并且不必使用与Membership服务相同的机制。为此,Membership和Role Manager的提供程序模型完全不同。


图7 添加角色

    如果您曾经在ASP.NET中实现了自定义角色,则一定会欣赏内置的Role Manager,因为您不再需要成为ASP.NET HTTP管道的主管,就能实现基于角色的安全性。一旦您添加了一些角色,您就可以回到default.aspx,并且可以使用LoginView控件来做一些有趣的事情。在<loggedintemplate/>元素之后添加另一个部分:

<rolegroups>
    <asp:rolegroup roles='ForumModerators'>
        <contenttemplate>
            <h4>Controls for forum moderators go here.</h4>
        </contenttemplate>
    </asp:rolegroup>
    <asp:rolegroup roles='Friends'>
        <contenttemplate>
            <h4>Welcome, friend!</h4>
        </contenttemplate>
    </asp:rolegroup>
</rolegroups>

    您可能没有选择与我相同的角色,因此,需要用您自己的角色名来代替我的角色名,并且调整内容使之适合角色。完成后,就可以通过使用不同角色中的不同用户帐户登录来检验您的新页面,并且观察当角色改变时页面的内容如何改变。请注意,如果两个角色组都与用户的角色相匹配,则总是显示第一个匹配的角色组(从上到下)。

    虽然这并不新鲜,但是请您记住,您始终可以通过User.IsInRole以编程方式测试角色。还需要谨记的是,您可以使用web.config中的部分来准许或拒绝访问各个页面,如下所示:

<authorization>
    <deny users='?'/>
    <allow roles='ForumModerators'/>
    <deny users='*'/>
</authorization>

    第一项告诉ASP.NET禁止传入任何未经身份验证的请求(强制执行身份验证)。第二项和第三项确保只有ForumModerators可以访问web.config文件所驻留的目录树中的内容。记住,授权部分可用于子目录中的web.config文件,也可以用于元素,以控制对单独文件的访问。

PasswordRecovery密码恢复控件

  该控件可以实现简单地密码提示恢复功能,并将密码email给用户,代码如下:

<asp:PasswordRecovery id="prForgotPass" runat="server"> </asp:PasswordRecovery>


  实现时如下图所示:


  用户输入用户名后,则需要输入密码恢复的答案,如下图:


  并在该控件中设置将恢复后的密码EMAIL给用户

<asp:PasswordRecovery id="prForgotPass" runat="server">
<MailDefinition
BodyFileName="forgotpassword.txt"
From="helpdesk@dorknozzle.com"
Subject="Word has it, you forgot your password?"/>
</asp:PasswordRecovery>


  ChangePassword 更改密码控件

  更改密码控件可以很方便地实现密码的更改,包括一个 MailDefinition 属性。如果将值赋给 MailDefinition 属性,则 ChangePassword 控件在密码成功更改时将自动向用户发送一个电子邮件。  

  直接使用Membership API

  有时候,需要对成员身份进行更高级别(比 Web 站点管理工具或 Login 控件提供的控制级别高)的控制。在这些情况下,可以直接使用 Membership API。

  Membership API 是通过 Membership 类公开的。Membership 类包含的方法使您能够完成以下工作:创建新用户;更改密码;搜索与特定条件匹配的用户等等。在幕后,Login 控件使用这些方法与已配置的成员身份提供程序进行交互。

  下面列出了 Membership 类的一些比较重要的方法:

   CreateUser — 使您能够创建新用户。

   DeleteUser — 使您能够删除现有用户。

   FindUsersByEmail — 使您能够检索与某个特定的电子邮件地址匹配的用户集合。

   FindUsersByName — 使您能够检索与某个特定的用户名匹配的用户集合。

   GeneratePassword — 使您能够生成随机密码。

   GetAllUsers — 使您能够检索成员身份提供程序中存储的所有用户。

   GetNumberOfUsersOnline — 使您能够返回当前正在访问 Web 应用程序的用户数。

   GetUser — 使您能够检索与当前用户相关联的成员身份信息,或者使您能够检索与某个已经提供用户名的用户相关联的成员身份信息。

   GetUsernameByEmail — 使您能够检索具有某个特定电子邮件地址的用户的用户名。

   UpdateUser — 使您能够更新特定用户的信息。

   ValidateUser — 使您能够根据成员身份提供程序对用户进行身份验证。

  最后,我们利用该API,实现稍微复杂的功能。在index.aspx中,添加一个label控件,显示在线的人数,代码如下:

Number of Users Online:
<asp:Label id="lblNumUsersOnline" runat="server"/>

  在PAGE-LOAD事件中,增加代码如下:

Sub Page_Load(s As Object, e As EventArgs) Handles MyBase.Load
lblNumUsersOnline.Text = _
Membership.GetNumberOfUsersOnline().ToString()
End Sub

  则可以显示在线人数。我们并且再添加一个gridview,显示所有用户的在线状况,是否允许某种权限和相关的信息。

<asp:GridView id="gvUsers" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField HeaderText="Username" DataField="Username" />
<asp:BoundField HeaderText="Is Online?" DataField="IsOnline" />
<asp:BoundField HeaderText="Is Approved?" DataField="IsApproved" />
<asp:BoundField HeaderText="Email" DataField="Email" />
</Columns>
</asp:GridView>

  并将gridview绑定:

gvUsers.DataSource = Membership.GetAllUsers() ;
gvUsers.DataBind();

  最后运行结果如下图:

  总结

  本文以实际例子,介绍了asp.net 2.0中新增加的登陆系列控件的简单用法以及新增加的Membership API的简单使用,更多的请参考MSDN或正式版时的有关介绍。

阅读更多
换一批

没有更多推荐了,返回首页