Security Tutorials系列文章第四章:在SQL Server里创建Membership Schema

Security Tutorials系列文章第四章:在SQL Server里创建Membership Schema

导言:
    前2章我们考察了使用表单认证来对站点的访问者进行甄别.利用它,开发人员可以很容易的使访问者“登录”,并在页面间导航时通过票据“记住”他们.FormsAuthentication类包含了一些方法来创建票据并将票据添加到访问者的cookies里. 而FormsAuthenticationModule则对请求进行检查,对有效的票据,生成相关的GenericPrincipal 以及 FormsIdentity对象.

Forms authentication仅仅是一种机制,对登录的用户创建一个票据,在后续请求时,对票据进行解析以判断用户的身份.对一个支持用户帐户的web application而言,我们依然需要执行一个用户存储同时添加功能以验证credentials、注册新用户,以及其它一些与用户帐户相关的任务.

在ASP.NET 2.0以前。开发人员要自己处理所有的与用户帐户相关的任务.幸运的是,ASP.NET团队在ASP.NET 2.0里引入了Membership framework.该Membership framework就是.NET Framework里的一系列类,它提供了处理用户帐户核心任务的编程接口.该框架基于provider model基础之上,它允许开发人员根据自己的要求进行定制.

正如我们在第一章探讨的那样,.NET Framework内置了2个Membership provider,也即:ActiveDirectoryMembershipProvider和SqlMembershipProvider. 就像其名字暗示的那样,SqlMembershipProvider使用一个Microsoft SQL Server数据库.可能你已经想到了,SqlMembershipProvider期望用户存储数据库里有某些数据库表,视图、存储过程等.我们需要在选定的数据库里添加这些构架.

本文首先考察添加这些构架的技术,然后我们再来看该构架里的关键的表,探讨它们的目的和重要性.在文章结尾,我们看如何告知一个ASP.NET应用程序,它的Membership framework应该使用哪个provider.


第一步:将User Store放在哪里?

一个ASP.NET应用程序的数据通常是保存在一个数据库的多个表里的.当我们贯彻SqlMembershipProvider数据库构架时,我们必须决定是将Membership schema放在存储数据同一个数据库里,还是另外的一个数据库了.

我建议将Membership schema放在应用程序储存数据的数据库里,原因如下:

.Maintainability——将数据存储在一个数据库里的应用程序比将数据存储在2个数据库里的应用程序,在易懂性、易维护性、易配置性方面要好.

.Relational Integrity——将与Membership相关的表和应用程序要用到的表一同放在一个数据库里,那么我们才可能在这2种表之间建立外键约束(foreign key constraints)

     要将用户存储和应用程序数据分开存储在不同的数据库,只有在这种情况下才有意义:你有多个应用程序,每个程序各自用单独的数据库存储数据,且这些程序之间不共享用户存储.

创建一个数据库

在第二章里创建的应用程序并没有用到数据库,但现在因为用户存储的原因,我们需要用到数据库了.让我们创建一个数据库,并添加SqlMembershipProvider provider必需的构架(见第二步)
注意:
    本系列文章将使用Microsoft SQL Server 2005 Express Edition数据库来存储应用程序的表,以及SqlMembershipProvider schema.这是基于如下考虑的:首先,它是免费的;再者,SQL Server 2005 Express Edition数据库可以直接放在应用程序的App_Data文件夹里,这样的话我们就可以将数据库与应用程序一起打包成一个ZIP压缩文件,在重新部署的时候不需要额外的操作说明,或什么配置选项.如果你倾向于使用SQL Server的其它版本数据库,随你吧,本质上来说步骤都是一样的.SqlMembershipProvider schema与Microsoft SQL Server 2000的各种版本以及更高版本都兼容的很好.

在解决方案资源管理器里右击App_Data文件夹,选Add New Item项(如果你没有看到App_Data文件夹,在解决方案资源管理器里右键单击,选Add ASP.NET Folder,再选App_Data).在Add New Item对话框里,选添加一个新SQL Database,名为SecurityTutorials.mdf,在本文,我们将SqlMembershipProvider schema添加到该数据库里,在以后的文章里我们再创建其它的表来存储应用程序的数据.

 

图1:在App_Data文件夹里添加一个新的名为SecurityTutorials.mdf的数据库

在App_Data文件夹里添加数据库后,将自动的将该数据库包含在Database Explorer里(在Visual Studio的非Express版本里,叫Server Explorer).转到Database Explorer,展开我们刚刚添加的SecurityTutorials数据库.如果你在屏幕上没有看到Database Explorer,打开View菜单,选择Database Explorer,或按住Ctrl+Alt+S即可.如图2所示,该SecurityTutorials数据库是空的,没有表、视图、存储过程.

 

图2:数据库目前为空


第二步:将SqlMembershipProvider Schema添加到数据库

SqlMembershipProvider需要在用户存储数据库里有一系列特定的表、视图、存储过程.我们可以通过aspnet_regsql.exe工具来将这些必须的数据库对象添加到数据库,它位于%WINDIR%\Microsoft.Net\Framework\v2.0.50727\文件夹下.

注意:
     aspnet_regsql.exe工具是用来对某个具体的SQL Server数据库添加或移除ASP.NET application services的.该ASP.NET application services出来包含SqlMembershipProvider 和 SqlRoleProvider要用到的构架外,还包括其它的ASP.NET 2.0 frameworks的基于SQL的provider所需要的构架.对aspnet_regsql.exe工具而言,有2点要提出来:

.我们是否要添加或移除application services

.要从哪个数据库添加或移除application services schema

为了知晓使用哪个数据库,aspnet_regsql.exe工具要我们提供如下的信息:数据库所在服务器的名称,用于链接数据库的链接信息(security credential),以及数据库名称.如果你使用的是SQL Server的非Express版本,你自然清楚这些细节,因为这与你通过ASP.NET web页面访问数据库时所必须提供的连接字符串信息是一样的.然而,当使用App_Data文件夹里的SQL Server 2005 Express Edition 数据库时,要确定服务器和数据库的名称却有点棘手.

下一节我们就来考察如何确定这些信息,如果你没有使用SQL Server 2005 Express Edition数据库,那就跳过这一节,直接看后面的“Installing the Application Services” 部分.

为App_Data文件夹里的SQL Server 2005 Express Edition数据库确定服务器名和数据库名

      了使用aspnet_regsql.exe工具,我们需要知道服务器和数据库的名称.服务器的名字就是localhost\InstanceName.一般来说,其中的InstanceName为SQLExpress.不过,如果你是手动安装的SQL Server 2005 Express Edition数据库(也就是说,在你安装Visual Studio的时候没有自动的安装SQL Server 2005 Express Edition数据库),那么你有可能在安装的时候选用的是其它的什么InstanceName,而不是默认的SQLExpress.

    确定数据库名有点麻烦,App_Data文件夹里的数据库,其名称一般包含一个到该数据库文件的路径和一个全局唯一标识符(globally unique identifier).我们需要确定数据库名称,因为要通过aspnet_regsql.exe来添加application services schema.

     确定数据库名最简便的方法是使用SQL Server Management Studio.它有一个图像界面来管理SQL Server 2005数据库,不过它不支持SQL Server 2005 Express Edition数据库.还好,你可以下载免费的SQL Server Management Studio Express Edition.
注意:
    如果你安装的是SQL Server 2005的非Express版本,那么你就安装了一个Management Studio的完整版.你可以使用该完整版来确定数据库名称,和下面步骤一样.

      先关闭Visual Studio,再打开SQL Server Management Studio,连接到localhost\InstanceName,就像前面提到的那样,InstanceName为SQLExpress;在Authentication项里,选Windows Authentication.


图3:连接到SQL Server 2005 Express Edition实例


       连接到SQL Server 2005 Express Edition实例后,Management Studio将显示Databases、 Security设置、Server Objects等文件夹.如果你展开Databases标签,你将看到SecurityTutorials.mdf数据库并没有注册到数据库实例里——我们要将它添加进来.
     
             在Databases文件夹里右击,选Attach.这将打开Attach Databases对话框,在该对话框里点Add按钮,浏览查找到SecurityTutorials.mdf,再点OK.图4显示的是选中SecurityTutorials.mdf后的Attach Databases对话框.图5显示的是当成功的将数据库添加进来后的Management Studio的Object Explorer界面.


图4:添加SecurityTutorials.mdf数据库

 


图5:SecurityTutorials.mdf数据库出现在Databases文件夹里


  如图5所示,SecurityTutorials.mdf数据库的名称相当深奥难懂。让我们将其改为一个容易记的名字.在数据库上右击,选Rename,将其改名为SecurityTutorialsDatabase.这样做并没有实际的改变文件名,仅仅是改变了SQL Server用于鉴别该数据库的名称而已.


图6:将数据库重命名为SecurityTutorialsDatabase

 到此,我们已经确定了SecurityTutorials.mdf数据库文件的服务器名和数据库名:分别为
localhost\InstanceName 以及 SecurityTutorialsDatabase.我们现在将通过aspnet_regsql.exe工具来安装application services.


安装Application Services

为了使用aspnet_regsql.exe工具,在start菜单里选Run,输入%WINDIR%\Microsoft.Net\Framework\v2.0.50727\aspnet_regsql.exe,点OK.或者,你也可以在Windows资源管理器里定位到该文件,双击即可.这2种方法最终的结果都是一样的.

运行该aspnet_regsql.exe工具将打开ASP.NET SQL Server Setup Wizard图像界面.该向导使我们很容易的就对某个具体的数据库添加或移除ASP.NET application services.该向导的第一步,正如图7所示,指出了该工具的目的.

 

图7:利用ASP.NET SQL Server Setup Wizard来添加Membership Schema


该向导的第二步是询问我们是添加还是移除application services.由于我们想为SqlMembershipProvider添加所需的表、视图、存储过程,因此选“Configure SQL Server for application services”选项.如果你在以后想从数据库移除这些构架的话,只需要重新运行该向导,选“Remove application services information from an existing database”选项即可.

 

图8:选中“Configure SQL Server for Application Services”选项

     第三步是要我们提供这些数据库信息:服务器名称、认证信息、数据库名称.如果你一直按本文章一步步来进行操作的话,这些信息就该是

如下的值:
.Server: localhost\InstanceName
.Windows authentication
.Database: SecurityTutorialsDatabase

 

图9:输入数据库信息

   输入这些信息后,点Next.最后一步对接下来要采取的步骤进行罗列.点Next以安装该application services,最后点Finish完成向导.
注意:
    如果你是用Management Studio来添加的数据库并重命名的话,在重新打开Visual Studio之前,一定要在Management Studio里将该数据库删除(detach),并关闭Management Studio.要删除该SecurityTutorialsDatabase数据库的话,在数据库名称上右击鼠标,在Tasks菜单里选择Detach即可.

   一旦完成向导后,再次启动Visual Studio,打开Database Explorer.将Tables文件夹展开,你将看到一系列的以aspnet开头的表.同理,你在Views 和 Stored Procedures文件夹里也会看到一系列类似的视图和存储过程.这些数据库对象构成了我们需要的application services schema.我们将在第三步里探讨这些与membership 和 role相关的数据库对象.

 

图10:一系列的Tables, Views,Stored Procedures已经添加到数据库了


注意:
    aspnet_regsql.exe工具的图像界面安装的是所有的application services schema.但是如果你通过命令行来执行aspnet_regsql.exe的话,你可以指定具体安装(或移除)哪些application services模块.比如,如果你只想添加SqlMembershipProvider 和 SqlRoleProvider providers所需的表、视图、存储过程的话,那么你就可以在命令行运行aspnet_regsql.exe就行了.还有一种方法,你可以手动运行aspnet_regsql.exe要用到的某些T-SQL创建脚本.这些脚本位于WINDIR%\Microsoft.Net\Framework\v2.0.50727\文件夹下面,比如InstallCommon.sql, InstallMembership.sql, InstallRoles.sql, InstallProfile.sql, InstallSqlState.sql等等.

   到此,我们已经创建好了SqlMembershipProvider所需要的数据库对象.然而我们还需要告知Membership framework,它应该使用SqlMembershipProvider(而不是ActiveDirectoryMembershipProvider),且SqlMembershipProvider应该使用SecurityTutorials数据库.我们将在第四步进行详细的探讨,现在我们来对我们刚刚创建的这些数据库对象进行更深入的考察.

 

第三步:考察构架的关键数据库表

   当在ASP.NET应用程序里处理Membership 和 Roles frameworks时,具体的执行细节由provider进行了封装.在以后的文章里,我们将通过.NET Framework的 Membership 和 Roles classes来了解这些框架.当使用这些高层(high-level)API时,我们没有必要了解具体的底层细节,比如SqlMembershipProvider 和 SqlRoleProvider到底执行的什么查询,或改动的是哪些表等.

   虽然我们在对第二步里创建的这些数据库构架不了解的情况下,依然可以放心大胆的使用Membership 和 Roles frameworks.但是当我们创建表来存储应用程序的数据时,我们可能需要创建与users 或 roles相关的实体.如果我们在用于存储应用程序数据的表和第二步里创建的表之间建立外键约束的话,这将有助于熟悉SqlMembershipProvider 和 SqlRoleProvider schemas.此外,在某些极少数情况下,我们需要直接通过数据库处理user 和 role存储(而不是通过Membership 和 Roles类).


Partitioning the User Store Into Applications

Membership 和 Roles frameworks的设计思想是这样的:多个应用程序共用一个user和 role存储,每个使用Membership 或 Roles frameworks 的ASP.NET应用程序必须指定使用哪一部分.简单的说,多个应用程序将帐户和角色信息都存储在一个同一个user 和 role 存储里.图11显示的是,对user 和 role存储进行了分割,以供3个应用程序使用: HRSite, CustomerSite, 以及SalesSite,这3个应用程序每个都有自己的user和role信息,不过在物理上讲,这3个程序的帐户和角色信息都存储在相同的一个数据库表里.


图11

表aspnet_Applications就是用来定义如何分割的.该表的的每条记录对应的是使用该数据库来存储帐户信息的一个应用程序.该表有4列:ApplicationId, ApplicationName, LoweredApplicationName, 以及Description.其中,ApplicationId是该表的主键,而ApplicationName是该应用程序的名称.

  其它与Membership和Role相关的表都与aspnet_Applications表的ApplicationId列挂钩.比如,对aspnet_Roles和aspnet_Users表而言,它们都有一个ApplicationId外键.该ApplicationId外键指定了这些记录属于哪个应用程序.

 

存储User Account信息

   User account信息存储在2个表里:aspnet_Users以及aspnet_Membership.其中aspnet_Users表包含了最基本的User account信息,它有3个列:

.UserId
.UserName
.ApplicationId

  UserId是主键(唯一标识的),UserName的类型为nvarchar(256),它和密码一起构成了用户credentials(用户密码存储在aspnet_Membership表),而ApplicationId将user account与aspnet_Applications表里的某个应用程序对应起来.该表的UserName 和 ApplicationId列一起构成了一个UNIQUE约束,以应对多个应用程序使用相同UserName的情况.

   而aspnet_Membership表包含了额外的user account信息,比如用户密码、电子邮件、最后一次登录时间等.表aspnet_Membership也是以UserId作为主键,和aspnet_Users表一样,aspnet_Membership表也是通过ApplicationId列将记录与某个应用程序对应起来。此外,aspnet_Users表和aspnet_Membership表的记录是一一对应的,联系的纽带就是UserId列.


Securing Passwords

   Password信息是存储在aspnet_Membership表里的,SqlMembershipProvider允许我们以以下3种技术之一来进行存储:

.Clear——密码以明文的形式存储.我强烈建议你不要采用该选项,假如某个黑客或心还不满的员工闯入数据库,那么每个用户的信息就一览无余了.

.Hashed——密码经过单道哈希算法(one-way hash algorithm)进行处理,并生成一个salt值.该哈希值(连同salt值)一起存储进数据库

.Encrypted——密码经过某种加密后存储进数据库

   具体用哪种技术由Web.config文件里的SqlMembershipProvider配置选项而定.我们将在第四步探讨SqlMembershipProvider的配置选项,默认采用的是Hashed选项.


   负责存储密码的列有:Password, PasswordFormat,以及PasswordSalt,其中PasswordFormat列的类型为int,它代表了使用的是何种技术,0表示Clear、1表示Hashed、2表示Encrypted。而PasswordSalt列是一个随意生成的字符串值,与采用的哪种技术无关,当计算由哈希算法处理过的密码时才会用到PasswordSalt的值.最后,Password列才是真正的password数据,可能是明文的,或经过哈希处理过的,又或经过加密处理后的,这都取决于采用的哪种技术.表1显示的是分别用这3种技术来存储密码“MySecret!”时的情形.

 

表1:当存储密码时“MySecret!”,相关列的值
注意:
     SqlMembershipProvider具体使用的是哪种加密或哪种哈希算法由<machineKey>元素来定义,我们在第三章的第3步里探讨了其配置选项

Storing Roles and Role Associations

   Roles framework允许开发人员定义一系列的role,以及哪些用户属于哪些角色,为此我们要用到2个表:aspnet_Roles 和 aspnet_UsersInRoles.而aspnet_Roles表里的每一条记录对应的是某个应用程序的一个role.和表aspnet_Users差不多,aspnet_Roles表也有3列:

.RoleId
.RoleName
.ApplicationId

  RoleId是主键,RoleName的类型为nvarchar(256),而ApplicationId将user account与aspnet_Applications里的某个应用程序对应起来.而RoleName和ApplicationId列一起构成了一个UNIQUE约束,用于确保某个应用程序的role名称是唯一的.

aspnet_UsersInRoles表作为users 和 roles之间的“映射”.它只有2个列,UserId 和 RoleId,这2列构成一个主键.


第四步:指定Provider并对其配置进行定制

  所有支持provider模式的框架——比如Membership 和 Roles frameworks,其自身是不管执行的细节的,而是委托一个provider class来具体负责. 就拿Membership framework来说,是通过Membership class来定义管理user accounts的API的,不过Membership class也不直接与任何的user store打交道.相反,Membership class的方法将请求“踢给”配置好的provider——在此,我们使用的是SqlMembershipProvider.当我们调用Membership class的其中一个方法时,Membership framework如何知道将调用委托给SqlMembershipProvider呢?

   Membership class类有一个Providers属性,它是对Membership framework为我们注册好的某个provider classes的引用.每一个注册的provider都有一个相关的name和type.其中name以一种友好的方式对Providers collection集合里的某个provider进行引用,而type则标识了该provider class.此外,每个注册的provider可能包含配置项.就Membership framework而言,配置选项有passwordFormat 和 requiresUniqueEmail等.表2将SqlMembershipProvider要用到的配置选择详细罗列出来.


   Providers属性的内容是通过web应用程序的配置选项进行指定的.默认下,所有的web应用程序都有一个类型为SqlMembershipProvider,名为AspNetSqlMembershipProvider的provider.该默认的Membership provider是在machine.config(位于%WINDIR%\Microsoft.Net\Framework\v2.0.50727\CONFIG)里进行注册的:

<membership>   
  <providers>         
    <add name="AspNetSqlMembershipProvider"                                  type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"           
         connectionStringName="LocalSqlServer"                       
         enablePasswordRetrieval="false"              
         enablePasswordReset="true"            
         requiresQuestionAndAnswer="true"           
         applicationName="/"             
         requiresUniqueEmail="false"            
         passwordFormat="Hashed"               
         maxInvalidPasswordAttempts="5"      
         minRequiredPasswordLength="7"                                                           
         minRequiredNonalphanumericCharacters="1"        
         passwordAttemptWindow="10"          
         passwordStrengthRegularExpression=""/>    
    </providers>
</membership>

 
    如上面的代码所示,<membership>元素为Membership framework定义了配置设置,而<providers>子元素指定了注册的providers.我们可以通过<add> 或 <remove>来添加或删除Providers,而用<clear>元素来移除所有目前的注册providers.如上述的代码所示,machine.config添加了一个类型为SqlMembershipProvider,名为AspNetSqlMembershipProvider的provider.

除了name 和 type属性外,<add>元素还包括其他的属性.表2列出了SqlMembershipProvider的所有属性.
注意:
    表2里的默认值都是SqlMembershipProvider类里定义的默认值.我们注意到,上述AspNetSqlMembershipProvider的配置并不是都与SqlMembershipProvider类的默认值一致.比如,requiresUniqueEmail默认时为true,但AspNetSqlMembershipProvider将其设置为false.


表2:Membership和SqlMembershipProvider的配置选项


   除了AspNetSqlMembershipProvider外,我们还可以在Web.config里依据application-by-application的原则添加其它类似的Membership providers.
注意:
    对Roles framework而言,处理方式是一样的.在machine.config里有一个注册的默认的role provider,我们可以在Web.config文件里对该role provider进行定制.我们将在后面的文章里详细的进行探讨.


定制SqlMembershipProvider Settings


   默认的SqlMembershipProvider (也就是AspNetSqlMembershipProvider)的connectionStringName属性被设置为LocalSqlServer.同理, LocalSqlServer也是在machine.config文件里定义的.

<connectionStrings>    
 <add name="LocalSqlServer"    
      connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"        providerName="System.Data.SqlClient"/>
</connectionStrings>


   正如我们看到的,连接字符串定义的是位于“|DataDirectory|aspnetdb.mdf”的一个SQL 2005 Express Edition数据库。字符串“|DataDirectory|”代表的是~/App_Data/ 目录,因此实际指向的是~/App_Data/aspnet.mdf.

   如果我们不在Web.config文件里指定Membership provider信息,那么应用程序将使用默认的AspNetSqlMembershipProvider.如果~/App_Data/aspnet.mdf数据库不存储,ASP.NET runtime也会自动的创建.然而,我们不想使用该aspnet.mdf数据库,而是我们在第二步创建的SecurityTutorials.mdf数据库.为此,我们可以通过2种方式来进行修改:

.在Web.config文件里为LocalSqlServer连接字符串指定一个值.重新指定值后,我们就可以让默认的AspNetSqlMembershipProvider使用SecurityTutorials.mdf数据库了.具体细节可参阅Scott Guthrie发表的博文《Configuring ASP.NET 2.0 Application Services to Use SQL Server 2000 or SQL Server 2005》

.添加一个新的类型为SqlMembershipProvider的provider,将其connectionStringName设置为连接到SecurityTutorials.mdf数据库.当你想对其它的配置也进行定制的时候,该方法很棒.在我自己的项目里,我都是采用的这种方式.
   
   在我们添加新的provider并连接到SecurityTutorials.mdf数据库以前,我们先在Web.config文件的<connectionStrings>节点里添加一个恰当的连接字符串.下面的代码添加了一个名为SecurityTutorialsConnectionString的新连接字符串,它连接到的是App_Data文件夹里的SecurityTutorials.mdf数据库.

<configuration>    
  <connectionStrings>         
    <add name="SecurityTutorialsConnectionString"               
     connectionString="DataSource=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\SecurityTutorials.mdf;Integrated Security=True;User Instance=True"
         providerName="System.Data.SqlClient"/>    
  </connectionStrings>    
 
  <system.web>        
  ... Configuration markup removed for brevity ...    
  </system.web>
</configuration>

注意:
    如果你使用的是其他数据库的话,对连接字符串进行必要的改动.更多详情,请访问ConnectionStrings.com.

  
    接下来,在Web.config文件里添加如下的Membership配置代码.它注册了一个名为SecurityTutorialsSqlMembershipProvider的新provider:
<configuration>   
  <connectionStrings>        
     <add name="SecurityTutorialsConnectionString"   
                connectionString="DataSource=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\SecurityTutorials.mdf;Integrated Security=True;User Instance=True"  
      providerName="System.Data.SqlClient"/> 
    </connectionStrings>  
<system.web>      
  <membership defaultProvider="SecurityTutorialsSqlMembershipProvider">               <providers>                 
         <!--Add a customized SqlMembershipProvider -->           
         <add name="SecurityTutorialsSqlMembershipProvider"                                  

              type="System.Web.Security.SqlMembershipProvider"
              connectionStringName="SecurityTutorialsConnectionString"
              enablePasswordRetrieval="false"      
              enablePasswordReset="true" 
              requiresQuestionAndAnswer="true" 
              applicationName="SecurityTutorials" 
              requiresUniqueEmail="true"  
              passwordFormat="Hashed"  
              maxInvalidPasswordAttempts="5"
              minRequiredPasswordLength="7"  
              minRequiredNonalphanumericCharacters="1"
              passwordAttemptWindow="10" 
              passwordStrengthRegularExpression=""/> 
        </providers>  
  </membership>  
        ... Configuration markup removed for brevity ...  
 </system.web>
</configuration>


   上述代码除了注册新的SecurityTutorialsSqlMembershipProvider provider外,还将它作为默认的provider(通过<membership>元素的defaultProvider属性).记得Membership framework可以注册多个providers,因为在machine.config里AspNetSqlMembershipProvider是注册的第一个provider,因此,除非我们指派其他的provider,不然AspNetSqlMembershipProvider就是默认的provider.

   目前,我们的应用程序里注册了2个providers:也就是AspNetSqlMembershipProvider 和 SecurityTutorialsSqlMembershipProvider.不过在注册SecurityTutorialsSqlMembershipProvider provider之前,我们可以在<add>元素前面添加一个<clear />元素来清除掉所有以前注册的provider.这样将把AspNetSqlMembershipProvider清除掉,自然,SecurityTutorialsSqlMembershipProvider就会变成唯一注册的Membership provider了.如果你这样处理的话,你必须将SecurityTutorialsSqlMembershipProvider标记为默认的provider,因为它是我们唯一可以使用的Membership provider.关于<clear />用法的更多详情,请参阅文章《Using <clear /> When Adding Providers》.


   我们注意到SecurityTutorialsSqlMembershipProvider的 connectionStringName属性设置为我们刚刚添加的SecurityTutorialsConnectionString连接字符串名称; applicationName属性设置为“SecurityTutorials”;此外requiresUniqueEmail属性也设置为true.而其他的配置与AspNetSqlMembershipProvider的配置是一样的.不过,你可以按照你的设想尽情的进行配置,比如,为了增强密码的安全强度,你可以配置使用2个non-alphanumeric字符,而不是默认的一个;或者将密码最低长度由7位增加到8位.

注意:
    我们知道Membership framework允许把一个user store分割成多个“扇区”以存储多个应用程序的相关数据.而Membership provider的applicationName属性就明确的指定了provider是使用的哪个应用程序来处理user store.因此,很重要的一点就是我们必须显式地为applicationName配置一个值,因为如果我们没有明确的为applicationName配置一个值的话,在运行时,applicationName属性会被赋值为应用程序的相对路径.如果应用程序的相对路径没有改变过,ok,一切正常;但是如果你将应用程序移动到另外一个路径的话,applicationName属性值自然就会跟着改变.如果发生了这种情况,Membership provider就会与user store的另一个“扇区”打交道,而不是之前的那个“扇区”.在移动应用程序之前创建的那些用户帐户依然存储在先前的那个“扇区”,自然,这些一来,先前创建的那些用户帐户就不再属于该应用程序所有了.对种情况的深入探讨,请参阅文章《Always Set the applicationName Property When Configuring ASP.NET 2.0 Membership and Other Providers.》


结语:
    到目前为止,我们有了一个被赋予application services的数据库(SecurityTutorials.mdf).并对我们的web应用程序进行了配置,使Membership framework使用我们刚刚注册的SecurityTutorialsSqlMembershipProvider provider.该注册的provider的类型为SqlMembershipProvider,其connectionStringName属性为相应的连接字符串——SecurityTutorialsConnectionString,并明确的设置了applicationName的值.

   我们将在应用程序里使用Membership framework了.在下篇文章,我们将考察如何创建新用户帐户.此外,我们还将探究如何甄别用户,执行基于用户的授权,以及如何在数据库存储与用户相关的其他信息.

祝编程快乐!


作者简介:

Scott Mitchell,著有七本ASP/ASP.NET方面的书,是4GuysFromRolla.com的创始人,自1998年以来一直应用微软Web技术。Scott是个独立的技术咨询顾问,培训师,作家,最近完成了将由Sams出版社出版的新作,《24小时内精通ASP.NET 2.0》。他的联系电邮为mitchell@4guysfromrolla.com,也可以通过他的博客http://ScottOnWriting.NET与他联系。

posted @ 2008-04-01 15:23  文涛  阅读(1096)  评论(1编辑  收藏  举报