使用ASP.NET Membership 和 角色管理器

使用ASP.NET Membership


通过ASP.NET Membership,我们可以创建用户、删除用户和编辑用户属性。所以这是一个实现登录相关控件的底层框架。
ASP.NET Membership的内容是在Forms鉴别完成后填入的。Forms鉴别提供的是一种验证用户的方法,而ASP.NET Membership的作用是表示用户的信息。
ASP.NET Membership使用的是提供器模式。ASP.NET Framework内包含了两个Membership提供器:
q SqlMembershipProvider——通过微软SQL Server数据库保存用户信息;
q ActiveDirectoryMembershipProvider——通过活动目录或活动目录应用程序模式服务器端保存用户信息。
在本节中,将介绍如何使用ASP.NET Membership API。介绍如何使用Membership类来通过编程方式修改Membership实例中表示的信息。
也还会介绍如何配置SqlMembershipProvider和ActiveDirectoryMembershipProvider。例如,将介绍如何修改有效的Memebership密码的必要条件。
最后,我们将构建一个自定的Membership提供器。这将是一个把成员信息保存在XML文件中的XmlMembershipProvider提供器。
2.3.1  使用Membership API
ASP.NET Membership提供的主要API是Membership类,该类支持下列方法:
q CreateUser——用于创建新用户;
q DeleteUser——用于删除已存在的用户;
q FindUsersByEmail——用于获得使用特定电子邮件地址的所有用户;
q FindUsersByName——用于获得使用特定用户名的所有用户;
q GeneratePassword——用于产生随机密码;
q GetAllUsers——用于获得所有用户;
q GetNumberOfUsersOnline——用于获得所有在线用户的人数;
q GetUser——用于通过用户名获得用户;
q GetUserNameByEmail——用于获得使用特定电子邮件地址的那位用户;
q UpdateUser——用于更新用户信息;
q ValidateUser——用于验证用户名和密码。
该类还支持下列事件:
q ValidatingPassword——当进行用户密码校验时触发,可以通过处理该事件来执行自定义的验证算法。
通过使用Membership类所提供的方法,可以对Web站点中的用户进行管理。例如,代码清单2-15中的页面显示了该应用程序中所有已注册用户的列表(见图2-5)。

图2-5  显示已注册用户
代码清单2-15  ListUsers.aspx
<%@ Page Language="C#" %>
<!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>List Users</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdUsers"
        DataSourceID="srcUsers"
        Runat="server" />

    <asp:ObjectDataSource
        id="srcUsers"
        TypeName="System.Web.Security.Membership"
        SelectMethod="GetAllUsers"
        Runat="server" />

    </div>
    </form>
</body>
</html>
在代码清单2-15中,ObjectDataSource控件被用来表示Membership类的数据源。通过调用Get- AllUser()方法可以得到所有用户的清单。
通过Membership类的方法可以创建自定义的Login控件。例如,可以通过调用GetNumberOfUser- Online()方法得到当前应用程序的在线用户数。代码清单2-16中的自定义控件显示该调用该方法所返回的值。
注解   在第12章将详细讨论自定义控件的创建问题。
代码清单2-16  UsersOnline.cs
using System;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace myControls
{
    /// <summary>
    /// Displays Number of Users Online
    /// </summary>
    public class UsersOnline : WebControl
    {
        protected override void RenderContents(HtmlTextWriter writer)
        {
            writer.Write(Membership.GetNumberOfUsersOnline());
        }
    }
}
代码清单2-17中的页面使用UsersOnline控件来显示了当前应用程序中在线用户的数量(见图2-6)。

图2-6  显示当前在线用户数量
代码清单2-17  ShowUsersOnline.aspx
<%@ Page Language="C#" %>
<%@ Register TagPrefix="custom" Namespace="myControls" %>
<!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>Show UsersOnline</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    How many people are online?
    <br />
    <custom:UsersOnline
        id="UsersOnline1"
        Runat="server" />

    </div>
    </form>
</body>
</html>
注解   如果某用户名在最近15分钟内被ValidateUser()、UpdateUser()或GetUser()方法所使用,那么我们就认为该用户在线。同时也可以通过修改Web配置文件中membership节点的userIsOnlineTimeWindow属性来更改15分钟这个默认的时间间隔设置。
有些Membership类的方法会返回一个或多个MembershipUser对象。MembershipUser对象用于表示一个特定Web站点成员,同时该类支持下列属性:
q Comment——用于将注解关联到当前用户上;
q CreationDate——用于获取当前用户的创建日期;
q Email——用于获取或设置当前用户的电子邮箱地址;
q IsApproved——用于获取或设置当前用户是否已被核准并且他的账号是否已激活;
q IsLockedOut——用于获取当前用户的锁定状态;
q IsOnline——用于确认当前用户是否在线;
q LastActivityDate——用于获取或设置当前用户最后活动的日期。该日期会在调用CreateUser()、ValidateUser()或GetUser()方法时自动更新;
q LastLockoutDate——用于获取当前用户最后被锁定的日期;
q LastLoginDate——用于获取当前用户最近一次登录该应用程序的时间;
q LastPasswordChangedDate——用于获得当前用户最近一次修改其密码的日期;
q PasswordQuestion——用于获得当前用户密码提示问题;
q ProviderName——用于获得关联到当前用户上的Membership提供器的名称;
q ProviderUserKey——用于获得一个关联于当前用户的唯一键值。在使用SqlMembershipProvider提供器时,该值为一个GUID;
q UserName——用于获得当前用户的用户名。
需要注意的是MembershipUser类中并不包含与用户密码和密码提示问题相关的属性。这是有意如此设计的,如果需要更改用户的密码,那么就需要到用该类所提供的方法。
MembershipUser类支持下列方法:
q ChangePassword——用于更改当前用户的密码;
q ChangePasswordQuestionAndAnswer——用于更改当前用户密码和密码提示问题;
q GetPassword——用于获得该用户的密码;
q ResetPassword——用于将当前用户的密码重置为随机产生的密码;
q UnlockUser——用于解锁被锁定的用户账号。
2.3.2  加密和散列用户密码
使用ASP.NET Framework的两个默认Membership提供器,我们可以有三种方式来保存用户的密码:
q Clear——密码以明文方式进行保存;
q Encrypted——保存密码之前对其进行加密处理;
q Hashed——原始密码不会被保存,而只保存密码的散列值(这是默认设置)。
要配置如何对密码进行保存,需要设置Web配置文件中的passwordFormat属性。例如,代码清单2-18中的Web配置文件设置了SqlMembershipProvider以明文方式保存用户密码。
代码清单2-18  Web.Config
<?xml version="1.0"?>
<configuration>
    <system.web>
      <authentication mode="Forms" />
      <membership defaultProvider="MyProvider">
        <providers>
          <add
            name="MyProvider"
            type="System.Web.Security.SqlMembershipProvider"
            passwordFormat="Clear"
            connectionStringName="LocalSqlServer"/>
        </providers>


      </membership>
    </system.web>
</configuration>
属性passwordFormat的默认值是Hashed。在此默认情况下,实际的密码不会保存在应用程序的任何地方。而是根据该密码所生成的散列值被保存了起来。
注解   散列算法会为每一个输入数据生成一个唯一的散列值。另外,散列算法最大的特点是,该过程是单向不可逆的。虽然我们可以很容易地根据任何值来生成其散列值,然而几乎不太可能再从散列值来确定其原始值。
保存密码散列值的优势是,即使黑客入侵了我们的Web站点,他也不能窃取到任何人的密码。然而同时也会有不足,就是谁也不能重新得到用户密码。例如,在这样的系统中,就不能使用PasswordRecovery控件来通过电子邮件向其用户发送原始码。
除了对密码进行散列之外,另一个保护密码的方法就是对其进行加密处理。加密密码的缺点是,它会比散列密码需要更强的运算处理逻辑。不过这样也有优点,可以根据加密信息为用户重新找回其原始密码。
代码清单2-19中的Web配置文件设置SqlMembershipProvider对密码进行加密保存。不过需要注意的是Web配置文件包含了一个machineKey节点,当要对密码加密时,必须为其decryptionKey属性提供一个明确的密钥。
注解   关于machineKey节点的更多信息,见2.1.4节。
代码清单2-19  Web.Config
<?xml version="1.0"?>
<configuration>
  <system.web>
    <authentication mode="Forms" />
    <membership defaultProvider="MyProvider">
      <providers>
        <add
          name="MyProvider"
          type="System.Web.Security.SqlMembershipProvider"
          passwordFormat="Encrypted"
          connectionStringName="LocalSqlServer"/>
      </providers>
    </membership>
    <machineKey
        decryption="AES"
        decryptionKey="306C1FA852AB3B0115150DD8BA30821CDFD125538A0C606DACA53DBB3C3E0AD2" />
  </system.web>
</configuration>
注意   确认在使用代码清单2-19中的Web配置文件之前,修改了decryptionKey属性的值。我们可以使用在2.1.4节中讨论的GenerateKeys.aspx页面来生成新的decryptionKey。
2.3.3  修改用户密码条件
          在默认情况下,密码至少需要包含7个字符和1个非文字数字的字符(比如*、_或!等既不是字母也不是数字的字符)。我们可以通过设置以下三个Membership提供器的属性来修改密码设置策略:
q minRequiredPasswordLength——密码最小长度限制(默认值是7);
q minRequiredNonalphanumericCharacters——密码中非文字数字字符的最少个数(默认值是1);
q passwordStrengthRegularExpression——密码必须要正确匹配的正则表达式模式(默认值是一个空字符串)。
属性minRequiredNonalphanumericCharacters常常会使得用户很迷惑,因为大多数Web站点的用户并不熟悉所谓必须输入非文字数字的这一要求。代码清单2-20中的Web配置文件展示了在使用SqlMembershipProvider时如何禁用这一要求。
代码清单2-20  Web.Config
<?xml version="1.0"?>
<configuration>
  <system.web>
    <authentication mode="Forms" />
    <membership defaultProvider="MyProvider">
      <providers>
        <add
          name="MyProvider"
          type="System.Web.Security.SqlMembershipProvider"
          minRequiredNonalphanumericCharacters="0"
          connectionStringName="LocalSqlServer"/>
      </providers>
    </membership>
  </system.web>
</configuration>
2.3.4  锁定坏用户
在默认情况下,如果有用户在10分钟内连续5次输入了错误的密码,那么该账号将被自动锁定。也就是说,该账号就不能正常登录了。
同样,如果用户在10分钟内连续5次输入了错误的密码提示问题,那么该账号也将被锁定。也就是说,在一定的时间间隔内每位用户最多只能进行5次密码和5次密码提示的确认尝试(这两个限制的次数是独立进行计数的)。
下面两个配置选项将控制账号在什么条件下会被锁定:
q maxInvalidPasswordAttempts——在一定时间间隔内允许用户最多可以输入的错误密码和错误密码提示问题的次数(默认值是5);
q passwordAttemptWindow——用于设置以分钟为单位的时间间隔,在该时间间隔内用户输入了过量的错误密码或错误的密码提示问题,那么该账号就将被锁定。
代码清单2-21  Web.Config
<?xml version="1.0"?>
<configuration>
  <system.web>
    <authentication mode="Forms" />
    <membership defaultProvider="MyProvider">
      <providers>
        <add
          name="MyProvider"
          type="System.Web.Security.SqlMembershipProvider"
          maxInvalidPasswordAttempts="3"
          passwordAttemptWindow="60"
          connectionStringName="LocalSqlServer"/>
      </providers>
    </membership>
  </system.web>
</configuration>
当一个用户被锁定后,必须调用MembershiptUser.UnlockUser()方法来重新启用该用户的账号。代码清单2-22中的页面用于输入特定用户名并对其进行解除锁定(见图2-7)。

图2-7  移除对账号的锁定
代码清单2-22  RemoveLock.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void btnRemove_Click(object sender, EventArgs e)
    {
        MembershipUser userToUnlock = Membership.GetUser(txtUserName.Text);
        if (userToUnlock == null)
        {
            lblMessage.Text = "User not found!";
        }
        else
        {
            userToUnlock.UnlockUser();
            lblMessage.Text = "Lock removed!";
        }
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Remove Lock</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:Label
        id="lblUserName"
        Text="User Name:"
        AssociatedControlID="txtUserName"
        Runat="server" />
    <asp:TextBox
        id="txtUserName"
        Runat="server" />
    <asp:Button
        id="btnRemove"
        Text="Remove Lock"
        Runat="server" OnClick="btnRemove_Click" />
    <br />
    <asp:Label
        id="lblMessage"
        EnableViewState="false"
        Runat="server" />
    </div>
    </form>
</body>
</html>
2.3.5  配置SQLMembershipProvider提供器
SQLMembershipProvider是默认的Membership提供器。除非额外进行配置,否则在应用程序的App_Data文件夹中的微软SQL Server精简版数据库ASPNETDB.mdf将用来保存成员信息。该数据库第一次使用在Membership时自动创建。
如果希望将成员信息保存到其他的微软SQL Server数据库中,那么需要执行以下两个步骤:
q 将必要的数据库对象添加到微软SQL Server数据库中;
q 配置应用程序使用新的数据库。
为了完成第一个步骤,需要使用aspnet_regsql命令行工具。该工具位于如下文件夹中:
\WINDOWS\Microsoft.NET\Framework\v2.0.50727
注解   如果直接打开了SDK命令行工具,那么在使用aspnet_regsql命令之前就不再需要手动定位到Microsoft .NET文件夹了。
如果不带参数执行aspnet_regsql命令,那么将会启动ASP.NET SQL Server安装向导(见图2-8)。通过该向导可以选择数据库并自动安装Membership对象。
此外,还可以选择通过执行下列两个SQL批处理文件来代替使用aspnet_regsql工具,以安装Membership相关数据库项:
\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallCommon.sql
\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallMembership.sql
如果你不希望在你的数据库服务器端上安装.NET Framework,那么就可以执行这些SQL批处理文件。
当数据库已配置来支持ASP.NET Membership之后,还必须配置应用程序以使其在使用Membership时能连接到该数据库上。代码清单2-23中的Web配置文件用于连接到位于MyServer服务器端上名为MyDatabase的数据库。

图2-8  使用ASP.NET SQL安装向导
代码清单2-23  Web.Config
<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="MyConnection" connectionString="Data Source=MyServer;Integrated Security=True;Initial
Catalog=MyDatabase"/>
  </connectionStrings>
  <system.web>
    <authentication mode="Forms" />
    <membership defaultProvider="MyMembershipProvider" >
      <providers>
        <add
          name="MyMembershipProvider"
          type="System.Web.Security.SqlMembershipProvider"
          connectionStringName="MyConnection" />
      </providers>
    </membership>
  </system.web>
</configuration>
在代码清单2-23中,配置了一个名为MyMembershipProvider的新Membership默认提供器。新的成员提供器使用的连接字符串名值为MyConnection。而MyConnection被定义在配置文件的上部的connectionStrings节点中。该连接字符串表示了连接到位于MyServer服务器端上名为MyDatabase的数据库。
2.3.6  配置ActiveDirectoryMembershipProvider
ActiveDirectoryMembershipProvider是包含在ASP.NET Framework中的另一个Membership提供器。通过这个提供器,我们可以将用户的信息保存在活动目录或ADAM(应用模式活动目录)。
ADAM是活动目录的一个轻量级版本实现。我们可以从微软Web站点(http://www.microsot. com/adam)上下载ADAM。ADAM兼容于微软Windows Server 2003和微软Windows XP Professional(Service Pack1)这两类操作系统。
如果要在ADAM上使用ASP.NET Membership,那么必须要完成以下两个步骤:
(1) 创建ADAM实例和必须的相关类。
(2) 配置应用程序使用ActiveDirectoryMembershipProvider,并将其连接到ADAM实例上。
在接下来的部分中,将按这些步骤依次进行检验。
1. 配置ADAM
首先,需要安装ADAM新实例。在下载并安装好ADAM后,请遵循以下步骤进行设置:
(1) 通过从程序组ADAM菜单中选择创建ADAM实例,将会运行应用模式活动目录安全向导(见图2-9)。
(2) 在安装程序的选项设置步骤中,选择创建唯一实例选项。
(3) 在实例名设置步骤中,输入名称:WebUsersInstance。
(4) 在端口设置步骤中,使用默认的LDAP和SSL端口号(389和636)。
(5) 在活动目录分区设置步骤中,创建一个名为O=WebUsersDirectory的新目录应用分区。
(6) 在文件位置设置步骤中,使用默认的数据文件位置。
(7) 在运行服务账号选取步骤中,选择账号Network Service。
(8) 在ADAM管理设置步骤中,为管理员账号选择Currently Logged on User选项。
(9) 在导入LDIF文件步骤中,选择MS-AZMan.ldf、MS-InetOrgPerson.ldf、MS-User.ldf和MS-UserProxy.ldf。
当完成了上这些步骤之后,一个名为WebUsersInstance的新ADAM实例就创建好了。下一步是配置ADAM管理员账号,遵循下列步骤。

图2-9  创建新的ADAM实例
注意   如果使用Windows XP系统,SSL证书默认并未安装,那么就需要再执行一些额外的安装步骤。否则,当尝试重置用户密码是会收到一个错误提示。
在默认情况下,通过非安全连接方式连接到ADAM实例将不能执行与密码相关的操作。可以通过ADAM自带工具dsmgmt.exe来解除该限制。打开ADAM命令行工具并输入如下一系列命名:
(1) 输入dsmgmt。
(2) 输入ds behaior。
(3) 输入connections。
(4) 输入connect to server localhost:389。
(5) 输入quit。
(6) 输入allow passwd op on unsecured connection。
(7) 输入quit。
如果没有使用SSL连接,那么密码将以明文方式传输。需要注意的是,一定不要在实际的产品中使用这样的部署方式。
(1) 通过程序组ADAM菜单运行ADAM ADSI Edit应用程序(见图2-10)。
(2) 通过选择菜单选项:Action(动作)→Connect to(连接到),打开连接设置对话框。
(3) 在连接设置对话框中,通过使用特别的名称并输入名称O=WebUsersDirectory,来选择连接到节点的选项。然后点击OK。
(4) 展开新连接并选中O=WebUsersDiretory节点。
(5) 选择菜单选项:Action(动作)→New(新建)→Object(对象)。
(6) 在创建对象对话框中,选择organizationalUnit类并将新类命名为WebUsers。
(7) 选中OU=WebUsers节点,并选择菜单选项:Action(动作)→New(新建)→Object(对象)。
(8) 在创建对象对话框中,选择用户类并将新类命名为ADAMAdministrator。
(9) 选中CN=ADAMAdministrator,并选择菜单选项:Action(动作)→Reset Password(重置密码),然后输入密码secret_。
(10) 选中CN=Roles节点,并双击CN-Administrators节点。
(11) 最后用鼠标双击Member属性,并同时为ADAM账号ADAMAdministrator ( CN=ADAMAdmini- strator, OU=WebUsers, O=WebUsersDirectory ) 添加特殊名称。

图2-10  使用ADAM ADSI编辑器
完成了这一系列的设置步骤之后,ADAMAdministrator账号就配置完毕了。当应用程序从ActiveDirectoryMembershipProvider连接到ADAM实例时,将需要使用这个账号。
2. 配置ActiveDirectoryMembershipProvider
下一步是配置应用程序来使用ActiveDirectoryMembershipProvider。可以使用代码清单2-24中的Web配置文件来完成该配置。
代码清单2-24  Web.Config
<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add
      name="ADAMConnection"
      connectionString="LDAP://localhost:389/OU=WebUsers,O=WebUsersDirectory"/>
  </connectionStrings>
  <system.web>
    <authentication mode="Forms" />
    <membership defaultProvider="MyMembershipProvider">
      <providers>
        <add
          name="MyMembershipProvider"
          type="System.Web.Security.ActiveDirectoryMembershipProvider"
          connectionStringName="ADAMConnection"
          connectionProtection="None"
          connectionUsername="CN=ADAMAdministrator,OU=WebUsers,O=WebUsersDirectory"
          connectionPassword="secret_"
          enableSearchMethods="true" />
      </providers>
    </membership>
  </system.web>
</configuration>
代码清单2-24中的Web配置文件配置了一个名为MyMembershipProvider的新默认Membership提供器。该提供器是ActiveDirectoryMembershipProvider类的实例。
对于使用ActiveDirectoryMembershipProvider提供器所提供的一些属性需要额外的解释。其中connectionStringName属性所指的是定义在connectionStrings节点中的连接字符串。该连接字符串用于连接在端口389上进行侦听的本地ADAM实例。
需要注意的是connectionProtection属性值被设置为了None。如果不修改这个属性,那么就需要使用SSL连接。如果确认需要使用SSL连接,那么还必须修改连接字符串中的端口号(典型设置是使用端口636)。
在前一节的配置文件中,connectionUsername和connectionPassword属性使用的是ADAMAdmini- strator账号。如果不使用SSL连接,那么就必须提供connectionUsername和connectionPassword属性。
最后,需要注意的是在该提供器中定义了enableSearchMethods属性。如果要通过使用Web站点管理工具来对用户进行配置,那么就必须要包含该属性。
ActiveDirectoryMembershipProvider类支持以下几个操作活动目录的属性:
q connectionStringName——定义在connectionStrings节点中,用于指定连接到活动目录服务器端的连接的名称;
q connectionUsername——用于指定连接到活动目录服务的活动目录账号;
q connectionPassword——用于指定连接到活动目录服务的活动目录账号的密码;
q connectionProtection——用于指定是否对连接进行加密。可能的取值是None和Secure;
q enableSearchMethods——用于使ActiveDirectoryMembershipProvider类可以使用附加方法。在使用Web站点管理工具时必须启用这个属性;
q attributeMapPasswordQuestion——用于将Membership安全提示问题映射到活动目录的相应属性上;
q attributeMapPasswordAnswer——用于将Membership安全提示问题答案映射到活动目录的相应属性上;
q attributeMapFailedPasswordAnswerCount——用于将Membership MaxInvalidPasswordAttempts属性映射到活动目录的相应属性上;
q attributeMapFailedPasswordAnswerTime —— 用于将Membership PasswordAttemptWindow属性映射到活动目录的相应属性上;
q attributeMapFailedPasswordAnswerLockoutTime——用于将Membership PasswordAnswer- AttemptLockoutDuration属性映射到活动目录的相应属性上。
在完成了以上配置步骤后,就可以像使用SqlMembershipProvider提供器那样来使用ActiveDirectoryMembershipProvider。当使用Login控件时,用户将通过活动目录进行验证。而当使用CreateUserWizard控件时,新用户将被创建到活动目录服务器端中。
2.3.7  创建自定义Membership提供器
由于ASP.NET Membership使用提供器模式,所以可以很容易的通过创建自定义Membership提供器来对ASP.NET Membership进行扩展。这里主要有两类需要创建自定义Membership提供器的情形。
第一类,假设你已经拥有了一个ASP.NET 1.x或传统ASP实现的应用程序。并且当前的成员信息保存在完全自定义的数据库表结构中。此外,该数据库的表结构很难和SqlMembershipProvider所使用的数据库表结构进行映射关联。
在这种情况下,创建自定义Membership提供器来读取已有的数据库表结构是很有意义的。创建了这样的自定义Membership提供器后,就可以在已有数据库表结构上使用ASP.NET Membership。
第二类,假设你需要将用户信息保存在除了微软SQL Server数据库或活动目录服务器端以外的其他数据存储环境中。例如,有些公司或组织可能是会使用Oracle或DB2这类数据库服务器端。这样一来,就需要创建自定义Membership提供器来访问自定义的数据存储环境。
在本节中,我们会创建一个简单的自定义Membership提供器:将用户信息保存在XML文档中的XmlMembershipProvider。
不幸的是,如果将实现XmlMembershipProvider的所有代码都放在这里将会太占篇幅。不过这些代码已放置在本书随书附带资源中,该代码文件名为XmlMembershipProvider.cs,并位于App_Code文件夹中。
XmlMembershipProvider类继承至MembershipProvider抽象类。该抽象类提供了超过25个属性和方法需要实现。
例如,ValidateUser()方法需要被实现。因为Login控件在对用户名和密码进行校验时需要调用这个方法。
CreateUser()方法也需要被实现。当CreateUserWizard控件创建新用户时会调用这个方法。
用来配置XmlMembershipProvider的Web配置文件包含在代码清单2-25中。
代码清单2-25  Web.Config
<?xml version="1.0"?>
<configuration>
    <system.web>
      <authentication mode="Forms" />

      <membership defaultProvider="MyMembershipProvider">
        <providers>
          <add
            name="MyMembershipProvider"
            type="AspNetUnleashed.XmlMembershipProvider"
            dataFile="~/App_Data/Membership.xml"
            requiresQuestionAndAnswer="false"
            enablePasswordRetrieval="true"
            enablePasswordReset="true"
            passwordFormat="Clear" />   
        </providers>
      </membership>

    </system.web>
</configuration>

需要注意的是XmlMembershipProvider支持许多的属性。例如,它支持passwordFormat属性,该属性用于指定密码是以散列值形式还是以明文形式保存(该属性不支持对密码进行加密设置)。
该XmlMembershipProvider提供器将成员信息保存在一个名为Membership.xml的XML文件中,且该文件位于App_Data文件夹。如果需要,也可以手动将用户添加到该文件中。另外,还可以通过CreateUserWizard控件或Web站点管理工具来创建新用户。
代码清单2-26中包含了一个Membership.xml文件的示例。
代码清单2-26  App_Data\Membership.xml
<credentials>
  <user name="Steve" password="apple" email="[email protected]" />
  <user name="Andrew" password="secret" email="[email protected]" />
</credentials>
在随书资源提供的示例代码中,包括了一个Register.aspx页面、一个Login.aspx页面和一个ChangePassword.aspx页面。通过使用这些页面,可以对XmlMembershipProvider所提供的各种功能进行方便的试验。
注意   动态XPath查询可能会带来XPath注入攻击,就像动态SQL查询可能会带来SQL注入攻击一样。在编写XmlMembershipProvider类时,我们应该尽量避免使用诸如SelectSingleNode()这样的方法来避免XPath注入攻击,即时使用这些方法可能会得到更简单和高效的代码。但大多数时候,代码的安全性比快速开发和运行更为重要。
我们可以通过将用户分组到角色中,再将授权设置应用到相应角色上,以代替为单个用户分别进行授权设置。例如,我们可以对应用程序站点的某一部分内容进行密码鉴别保护,以确保只有管理员角色中的成员可以访问这些部分中的页面。
类似于ASP.NET Membership,角色管理器已经内建在当前的ASP.NET鉴别框架中。可以通过向一个或多个Web配置文件中添加authorization节点来配置角色鉴别规则。
此外,类似ASP.NET Membership,角色管理器也是使用的提供器模式。我们可以通过配置特定Role提供器来自定义角色信息保存在什么地方。
ASP.NET Framework提供了三个角色提供器:
q SqlRoleProvider——用于将角色信息保存在微软SQL Server数据库中;
q WindowsTokenRoleProvider——使用微软Windows用户组设置来表示角色信息;
q AuthorizationStoreRoleProvider——使用授权管理器将角色信息保存在诸如XML文件、活动目录或ADAM中。
在接下来的小节中,将会介绍如何配置每个Role提供器。还会介绍如何通编程方式来调用角色API,从而管理角色信息。
2.4.1  配置SqlRoleProvider
SqlRoleProvider是默认的角色提供器。使用SqlRoleProvider可以将角色信息保存到微软SQL Server数据库中。利用SqlRoleProvider可以创建自定义角色,所以你可以创建任何需要的角色。
SqlRoleProvider可以用于Forms鉴别和Windows鉴别中。当启用Forms鉴别后,就可以使用ASP.NET Membership来表示用户并把用户分派到特定的角色上。当启用Windows鉴别后,特定的Windows账号将被指派到自定义角色上。在这一节中,我们假设示例应用程序使用的是Forms鉴别。
注意   在启用Windows鉴别时,Web站点管理工具不支持将用户指派到角色上。所以在Windows鉴别被启用后,必须通过编程的方式来将用户指派到角色上。
代码清单2-27中的Web配置文件启用了SqlRoleProvider。
代码清单2-27  Web.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <roleManager enabled="true" />
    <authentication mode="Forms" />
  </system.web>
</configuration>
角色管理在默认情况下是禁用了的。代码清单2-27中的配置文件简单的启用了角色管理器。需要注意的是该配置文件同时还启用了Forms鉴别。
如果你不想自己编写代码清单2-27中的文件内容,那么你可以使用Web站点管理工具来创建它们。通过选取Visual Web Developer中的菜单选项:WebSite(站点)→ASP.NET Configuration(ASP.NET配置),从而打开Web站点管理工具。接下来,点击安全选项卡和启用角色连接(见图2-11)。
当启用了角色管理器后,还需要创建一些角色。我们有两种方法来创建角色。一是使用Web站点管理工具,或使用编程方式创建角色。
开启Web站点管理工具,并点击位于安全选项卡下的创建或管理角色连接。这样就可以开始进行角色创建了。我们假定你已经创建了一个名为Manager的角色。
当创建了一批角色后,还需要将用户指派到相应的角色中。同样,也可以使用Web站点管理工具来完成这个任务,或使用编程方式来做。
如果你的应用程序中还未曾创建任何用户,那么可以立即通过点击安全选项卡下的创建用户连接来创建新用户。需要注意的是在创建用户的同时,可以将该用户指派到一个或多个角色中(见图2-12)。也可以在随后的操作中,再通过点击创建或管理角色连接来为用户指派相应角色。




图2-11  通过Web站点管理工具启用角色

图2-12  为新用户指派角色


当创建好角色并将用户指派到这些角色上后,就可以在Web配置文件的authentication节点中使用这些角色了。例如,假设你的Web站点包含了一个名为SecretFiles的文件夹,并且你只希望属于Manager角色中的成员能访问该文件夹下的页面。代码清单2-28中的Web配置文件,会阻止除了属于Manager角色中的成员以外的任何用户访问SecretFiles文件夹。
代码清单2-28  Web.Config
<?xml version="1.0"?>
<configuration>
  <system.web>
    <authorization>
      <allow roles="Managers"/>
      <deny users="*"/>
    </authorization>
  </system.web>
</configuration>
代码清单2-28中的配置文件授予角色Managers对该目录有访问权限,并同时限制了除此角色以外的其他所有用户对该目录的访问权限。
如果你喜欢,也可以通过Web站点管理工具来管理授权。在后台,该工具会自动创建一个包含了authorization节点的Web配置文件(换句话说,该工具将完成我们之前刚做过的所有配置工作)。
在“安全”选项卡中,点击“创建访问规则连接”。从树视图中选中SecretFiles文件夹,再选择Managers角色并选中Allow(允许)选项(见图2-13)。点击确定按钮即会授权该角色的访问权限。接下来,再创建禁止非Managers角色访问用户信息的第二个授权。选择SecretFiles文件夹、选择All Users再选择Deny。点击确定按钮即添加新的权限授权。
通过SqlRoleProvider使用不同的数据库
在默认情况下,SqlRoleProvider同样是使用微软SQL Server精简版来作为ASP.NET Membership的数据库:AspNETDB.mdf。在应用程序根目录下的App_Data文件夹中会自动创建系统需要的数据库。

图2-13  为角色设置权限授权
如果要将角色信息保存到其他的微软SQL Server数据库中,那么必须要执行以下两个配置步骤:
q 配置数据库以使其包含必须的数据库对象;
q 配置应用程序以使其使用新的数据库。
在将角色信息保存到数据库中之前,还需要将必要的表和存储过程添加到数据库中。添加这些对象的最简单方法是使用aspnet_regsql命令行工具。该工具位于系统的下列目录中:
\WINDOWS\Microsoft.NET\Framework\[version]
注解   如果使用SDK中提供的命令行工具,则可以直接执行该命令,而不再需要通过命令行命令进入Microsoft.NET文件夹。
如果不带参数运行aspnet_regsql命令,那么ASP.NET SQL Server数据库安装向导将被打开(见图2-14)。我们也可以使用这个向导来连接到数据库,并自动地添加数据库对象。

图2-14  使用SQL Server数据库安装向导
另外,还可以通过执行下列两个SQL批处理文件来设置数据库。
q InstallCommon.sql
q InstallRoles.sql
以上批处理文件和aspnet_regsql工具位于同一文件夹中。
当安装完数据库对象后,还需要配置一个包含正确连接字符串的新SqlRoleProvider。代码清单2-29中的Web配置文件配置了一个名为MyRoleProvider的新提供器,以用来连接到MyServer服务器端中名为MyDatabase的数据库上。
代码清单2-29  Web.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add
      name="MyConnection"
      connectionString="Data Source=MyServer;Integrated Security=True;Initial Catalog=MyDatabase"/>
  </connectionStrings>
  <system.web>
    <authentication mode="Forms" />
    <roleManager enabled="true" defaultProvider="MyRoleProvider">
      <providers>
        <add
          name="MyRoleProvider"
          type="System.Web.Security.SqlRoleProvider"
          connectionStringName="MyConnection"/>
      </providers>
    </roleManager>
  </system.web>
</configuration>
代码清单2-29中的配置文件创建了一个名为MyRoleProvider的默认RoleManager。需要注意的是,MyRoleProvider提供器包含了指向MyConnection连接的connectionStringName属性。
2.4.2  配置WindowsTokenRoleProvider
当使用WindowsTokenRoleProvider提供器时,角色这个概念相当于微软Windows用户组。在使用WindowsTokenRoleProvider时,必须要启用Windows鉴别。而不能在WindowsTokenRoleProvider中使用Forms鉴别和ASP.NET Membership。
代码清单2-30中的配置文件将WindowsTokenRoleProvider配置为了默认的提供器。
代码清单2-30  Web.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <authentication mode="Windows" />
    <roleManager enabled="true" defaultProvider="MyRoleProvider">
      <providers>
        <add
          name="MyRoleProvider"
          type="System.Web.Security.WindowsTokenRoleProvider" />
      </providers>
    </roleManager>
  </system.web>
</configuration>
代码清单2-31中的页面包含了一个LoginView控件。LoginView控件将为Windows管理员组中的成员显示相对其他成员不同的内容(见图2-15)。
代码清单2-31  ShowWindowsRoles.aspx
<%@ Page Language="C#" %>
<!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>Show Windows Roles</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
   
    <asp:LoginView
        id="LoginView1"
        Runat="server">
        <RoleGroups>
        <asp:RoleGroup Roles="BUILTIN\Administrators">
            <ContentTemplate>
            <h1>Welcome Administrator!</h1>      
            </ContentTemplate>
        </asp:RoleGroup>
        </RoleGroups>
        <LoggedInTemplate>
            <h1>Welcome Average User!</h1>
        </LoggedInTemplate>
    </asp:LoginView>
   
    </div>
    </form>
</body>
</html>

图2-15  为Windows管理员组中的成员显示不同的内容
如果在启用了WindowsTokenRoleProvider后访问代码清单2-31中的页面,那么只有当用户是Windows管理员组中的成员时,才能看到上图中的内容。
2.4.3  配置AuthorizationStoreRoleProvider
授权管理器(AzMan)是Windows Server 2003提供的组件。使用授权管理器可以定义角色、任务和操作。
授权管理器比ASP.NET Framework中的授权框架支持更多的功能。例如,授权管理器支持角色继承,这样一来就可以很容易地基于已有的角色创建新角色。
注解   授权管理器也可以在Windows XP专业版上使用。当然,在使用之前必须要手动进行安装。Windows Server 2003提供的管理工具包需要从微软MSDN站点下载(http://msdn.microsoft.com)。
授权管理可以用三种方式存储角色信息。即可以使用XML文件、活动目录或应用模式活动目录(ADAM)来存储所创建的授权设置。
在使用ASP.NET Framework和授权管理器之前,需要创建授权存储。角色信息将会保存在位于当前应用程序中的XML文件中。请遵照下列步骤:
(1) 通过在命令提示行上执行AzMan.msc命令运行授权管理器(见图2-16)。
(2) 通过选取菜单选项:Action(动作)→Options(选项)→Developer mode(开发模式),来将授权管理器转换为开发模式。
(3) 通过选取菜单选项:Action(动作)→New Authorization Store(新授权存储),来开启新授权存储设置对话框。
(4) 选择XML文件选项,并在存储名输入框中输入应用程序的App_Data文件夹路径。例如:
c:\Websites\MyWebsite\App_Data\WebRoles.xml
(5) 通过右键点击刚才创建的授权存储名,并选择新应用程序菜单项来创建新的授权管理器应用。为应用起名为WebRoles并输入(其他的输入项可以空着)。

图2-16  使用授权管理器
当完成以上步骤后,授权管理器会将一个新的XML文件添加到该应用程序中。该XML文件中包含了授权存储。
接下来,还需要配置ASP.NET角色管理器以使用该授权存储。代码清单2-32中的Web配置文件使用了WebRoles.xml授权存储。
代码清单2-32  Web.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add
      name="AZConnection"
      connectionString="msxml://~/App_Data/WebRoles.xml"/>
  </connectionStrings>
  <system.web>
    <authentication mode="Windows" />
    <roleManager enabled="true" defaultProvider="MyRoleProvider">
      <providers>
        <add
          name="MyRoleProvider"
          type="System.Web.Security.AuthorizationStoreRoleProvider"
          connectionStringName="AZConnection"
          applicationName="WebRoles"
            />
      </providers>
    </roleManager>
  </system.web>
</configuration>
应该注意的是关于代码清单2-32中的配置文件所作的设置。首先,需要注意连接字符串所使用的msxml前缀:该前缀用于指出其连接字符串是表示的一个指向XML文件的连接路径。
第二,需要注意的是AuthorizationStoreRoleProvider中包含了一个applicationName属性。该属性必须包含我们在前面步骤中所创建的那个授权管理器应用的名字。
在完成了这些设置步骤以后,我们就可以像使用默认的SqlMembershipProvider那样来使用授权管理器。即可以通过Web站点管理工具或授权管理器界面来创建新的角色(见图2-17)。

图2-17  使用授权管理器创建新角色定义
2.4.4  在浏览器cookie中缓存角色
为了提高应用程序的性能,可以将用户角色信息缓存在浏览器cookie中。这样一来,角色管理器就不需要在每次用户访问页面时都对角色提供器进行查询。
将角色信息缓存在cookie中的功能默认是被禁用的。使用代码清单2-33中的Web配置文件可以开启这个功能。
代码清单2-33  Web.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <roleManager
      enabled="true"
      cacheRolesInCookie="true"
      createPersistentCookie="true" />
  </system.web>
</configuration>
代码清单2-33中的Web配置文件开启了角色缓存功能。此外,它是将角色信息缓存在了持久型cookie而不是会话型cookie中。
注意   当角色信息缓存在cookie中后,用户的缓存角色和用户的实际角色可能会有不同步的潜在风险。如果在服务器端上更新了用户的角色信息,已经执行的应用程序并不能立即获得以更新的角色。所以需要调用Roles.Deletecookie()方法来删除缓存中的无效cookie信息。
我们可以设置和角色cookie相关的很多属性:
q cacheRolesInCookie——用于设置是否将用户的角色信息缓存在浏览器cookie中(其默认值是false);
q cookieName——用于指定缓存角色信息cookie的名字(其默认值为.ASPXROLES);
q cookiePath——用于指定和该cookie相关联的路径(其默认值为/);
q cookieProtection——用于设置如何加密和验证缓存角色信息的cookie。其可能的取值是All、Encryption、None和Validation(其默认值为All);
q cookieRequireSSL——用于指定是否需要使用安全套接字(SSL)连接来传输缓存角色信息的cookie(其默认值为false);
q cookieSlidingExpiration——用于指定是否需要通过设置页面访问超时控制来保护cookie(其默认值为true);
q cookieTimeout——用于指定以分钟为单位的cookie超时时间(其默认值为30);
q createPersistentCookie——用于指定是否要使用持久型cookie来代替会话型cookie(其默认值为false);
q domain——用于指定与cookie相关的域(其默认值是空字符串);
q maxCachedResults——用于指定cookie中最多可以缓存多少角色信息(其默认值为25)。
2.4.5  使用Roles API
Roles类公开了管理角色的主要API。如果要通过编程方式来创建角色、删除角色或将用户配备到角色上,那么就需要使用Roles类所提供的方法。
Roles类提供了下列方法:
q AddUsersToRole——用于将一组用户添加到特定角色中;
q AddUsersToRoles——用于将一组用户添加到一组角色中;
q AddUserToRole——用于将某一用户添加到特定角色中;
q AddUserToRoles——用于将某一用户添加到一组角色中;
q CreateRole——用于创建新的角色;
q DeleteCookie——用于删除保存角色信息的cookie;
q DeleteRole——用于删除特定的角色;
q FindUsersInRole——用于返回在某一角色中包含了特定用户名的一组用户;
q GetAllRoles——用于返回所有角色的列表;
q GetRolesForUser——用于返回某一用户所属的所有角色的列表;
q GetUsersInRole——用于返回某一角色中的所有用户的列表;
q IsUserInRole——用于判断某一特定的用户是否是某一特定角色中的成员;
q RemoveUserFromRole——用于从某一特定角色中移出某一特定的成员;
q RemoveUserFromRoles——用于从一组角色中移出某一特定成员;
q RemoveUsersFromRole——用于从某一特定组角色中移出一组成员;
q RemoveUsersFromRoles——用于从一组角色中移出一组成员;
q RoleExists——用于判断一个特定的角色是否存在。
代码清单2-34中的页面示例了如何使用Roles类的方法。Page_Load()方法创建了名为Sales和Managers的两个角色(如果它们还未存在)。接下来,把当前用户分配到了这两个角色中。页面正文中的GridView控件显示了当前用户所属的所有角色的列表(见图2-18)。
代码清单2-34  ShowRoles.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    void Page_Load()
    {
        // If user is not authenticated, redirect to Login page
        if (!Request.IsAuthenticated)
        {
            FormsAuthentication.RedirectToLoginPage();
            Response.End();
        }
        // Create two roles
        if (!Roles.RoleExists("Managers"))
            Roles.CreateRole("Managers");
        if (!Roles.RoleExists("Sales"))
            Roles.CreateRole("Sales");
       
        // Add current user to both roles
        if (!Roles.IsUserInRole("Managers"))
            Roles.AddUserToRole(User.Identity.Name, "Managers");
        if (!Roles.IsUserInRole("Sales"))
            Roles.AddUserToRole(User.Identity.Name, "Sales");
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show Roles</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
   
    <h1>Your Roles</h1>
   
    <asp:GridView
        id="grdRoles"
        DataSourceID="srcRoles"
        EmptyDataText="You are not a member of any roles"
        GridLines="none"
        Runat="server" />
   
    <asp:ObjectDataSource
        id="srcRoles"
        TypeName="System.Web.Security.Roles"
        SelectMethod="GetRolesForUser"
        Runat="server" />
   
    </div>
    </form>
</body>
</html>

图2-18  显示用户所属的角色

From:

http://dev.firnow.com/course/4_webprogram/asp.net/netjs/2007921/72849.html

你可能感兴趣的:(asp.net)