考察ASP.NET 2.0Membership,Roles,Profile - Part 1
导言: 很多网站都有一个共同点:提供用户账号(user accounts),这些网站允许(或需要)访问者创建账号以使用特定的功能.比如ASPMessageboard.com网站,匿名用户和注册用户都可以使用搜索功能,但如果要发表文章或回复消息的话,访问者就必须获得用户账号并登录网站. 一个支持用户账号的网站,都要包括这些相同的步骤:创建一个数据库表来存储用户账号信息;创建一个登录页面;定义一种系统,当页面回传时仍可保存注册用户的登录状态;指出哪些页面只对注册用户可见;创建一个新页面供新用户注册;创建一个页面便于网站管理员管理用户账号等等.在ASP.NET之前,开发者必须决定如何贯彻这些方面。ASP.NET引入了forms-based authentication的概念,二楼提供了一个FormsAuthentication class类来便于在网站登录或注销.同样的还有authentication ticket,在页面请求时保存用户的登录状态。(关于ASP.NET的基于窗体的识别功能,请参阅文章《Using Forms Authentication in ASP.NET 》和《Dissecting Forms Authentication》) 即便有了基于窗体的识别功能,ASP.NET开发者仍然要定义并构建相应的结构来存储用户账号信息;创建登录和注销的web页面;还有便于访问者创建新账号和便于管理员管理账号的页面等.感谢ASP.NET 2.0,利用其membership system 和 security Web控件,可以减轻开发者的负担.简单的说,membership是一个API,可以编程访问与用户帐户相关的任务.比如:有专门的方法创建一个新的用户帐户,识别用户身份,删除用户,返回所有的用户信息等.此外,还有很多建立在该API基础上的security Web控件. 本系列文章将考察2.0版本中的membership, roles,pofile系统,以及各种security Web控件.本文我们考察membership的基本原理并配置与使用内置的SqlMembershipProvider. 基于窗口的认证(Authentication) 在ASP.NET之前,web开发者必须自己定义所有的认证.一个问题是当页面请求时如何记住用户的登录状态,也就是说当用户输入用户名和密码并成功登录后,当用户访问其它页面时,网站如何记得该用户是否成功登录了?另一个问题是如何防止页面被未授权的用户访问,也就是说如何设置一个页面只能被一部分或被授权的用户访问?在经典ASP里我们通过使用Session变量来解决这些问题.每个页面检查该Session变量一确定访问者的身份,以及是否允许他们访问. 为此,ASP.NET 1.0版提供了对forms-based authentication的支持,同时可以在Web.config文件里指定authorization rules角色.forms-based authentication提供了一种方法将authentication ticket作为一个cookie存储在浏览器里,在发生web请求的时候记住用户的登录状态.而FormsAuthentication class类包含了很多方法来处理这些authentication ticket。可以创建它也可以删除它. 不幸的是forms-based authentication依然留给开发者很多工作.开发者仍然必须决定如何传递用户帐户信息;仍然必须构建登录页面并编写使用FormsAuthentication class类的代码;仍然必须构建注销页面;构建注册帐户页面;构建管理帐户的页面等等。ASP.NET 1.0版本引入forms-based authentication的初衷是好的,只是贯彻起来有难度. 在Forms-Based Authentication之上构建ASP.NET 2.0的Membership Forms-based authentication依然存在于ASP.NET 2.0版本,使用方法与 1.x版本一样。同时在Web.config文件里也有authorization设置. ASP.NET 2.0版本增加的是membership API以及security Web控件. membership API是通过provider模式来执行的.那就意味着当定义好界面后可以对实际的执行过程进行定制.而.NET Framework包含了Membership class类,该类包含可很多方法,比如 CreateUser(), GetAllUsers(), ValidateUser()等等. 然而,当通过一个ASP.NET web应用程序来使用API的时候实际调用的类取决于应用程序的配置.你可以定制你的用户帐户逻辑,方法是创建一个执行定义好的membership API的类,然后配置web应用程序来使用你定义的类.当然,你也用不着定义一个自定义的类——ASP.NET包含了2个内置的membership providers,一个将用户帐户信息存储在一个SQL Server数据库;另一个使用的是实际的目录.因此membership system 和 security Web控件都可以被这2个内置的providers使用.如果你确实自定义了用户数据,你也可以创建一个自定义的provider通过相同的API 和 security控件来使用这些自定义的用户数据.关于provider模式的更多信息,请参阅文章《A Look at ASP.NET 2.0's Provider Model》(http://aspnet./articles/101905-1.aspx) 在本系列的后续文章里,我们将考察创建用户自定义的membership provider的详细步骤. SqlMembershipProvider——将用户帐户数据存储在一个SQL Server数据库 ASP.NET 2.0 里的SqlMembershipProvider provider使用一个数据库来存储认证信息.为了使用该provider,你必须创建一个相应的数据库构架(schema).可以通过2种方法来完成: 1.使用ASP.NET网站管理工具(这将会在一个SQL Server 2005数据库文件ASPNETDB.mdf里创建构架,且位于App_Data文件夹) 2.使用ASP.NET SQL Server注册工具——使用该工具在一个SQL Server 2000 或 2005 数据库里创建构架. ![]() 要使用ASP.NET网站管理工具的话,首先在Visual Studio的Website菜单,选择“ASP.NET Configuration”项,然后在Security项里,将authentication类型改为"From the internet" 。具体方法是:要么点"Select authentication type"链接,要么点" "Use the security Setup Wizard to configure security step by step"链接.这样将自动在App_Data文件夹里创建一个名为ASPNETDB.mdf的数据库(我们马上将考察该数据库的构架).关于使用网站管理工具的更多详情请参阅文章《Website Administration Tool Overview》(http://msdn2.microsoft.com/en-us/library/yy40ytx0.aspx) 如果你大算将用户账号信息存储在其它地方——比如App_Data文件夹之外的一个SQL Server 2000 或SQL Server 2005数据库. 那么你就需要使用ASP.NET SQL Server注册工具(aspnet_regsql.exe).该工具具有图像界面,当然你也可以通过命令行来使用它.你可以借助于图像界面指定在什么地方添加所需要的表.关于使用该工具的更多详情,请参阅技术文档(http://msdn2.microsoft.com/en-us/library/ms229862.aspx) 注意: 当你使用ASP.NET网站管理工具来将authentication类型设置为"From the internet" ,这将在Web.config文件里添加一行: ![]() <authentication mode="Forms" />
如果你通过ASP.NET SQL Server注册工具来创建数据库构架的话,你必须手工向Web.config文件添加在行。此外,没有在App_Data文件夹里的ASPNETDB.mdf数据库里创建构架,而是在其它数据库创建的,那么你必须要在Web.config文件里定制membership配置,并指定连接数据库的连接字符串. SqlMembershipProvider将用户帐户信息存储在下面2个表里: .aspnet_Users -每一个用户帐户一条记录,存储最基本的信息.其UserId列唯一的标识用户身份. .aspnet_Membership—该表的UserId列将该表与aspnet_Users表里的某条特定记录对应起来.aspnet_Membership表存储的是与用户账号相关的数据: Email, Password, 安全提示问题以及答案等等. 定制SqlMembershipProvider 如果你希望使用SqlMembershipProvider的默认配置(那就意味着用户帐户信息存储在App_Data文件夹里的ASPNETDB.mdf数据库里),那么你除了在Web.config文件里指定使用到的窗体认证和授权角色(authorization rules)外,不需要做其它的更改.(对于窗体认证和授权角色问题,可以通过ASP.NET网站管理工具来指定。关于在Web.config文件里指定authorization配置的更多信息请参阅文章《 Authentication and Authorization》(http://samples./quickstart/aspplus/doc/authandauth.aspx)和文章《Authorizing Users and Roles》(http://samples./quickstart/aspplus/doc/authorization.aspx) 如果你打算使用另外一个数据库,或者说改变membership的某些配置(比如:email地址是否是唯一的;密码的最短长度; 密码是纯文本还是经过加密;安全提示问题和答案是不是必需的等等)的话,你就要在Web.config文件里手动输入XML模块来指定你自己的用户配置.(注意:你必须要为applicationName设置一个“硬编码”值,有关这方面的更多详情请参阅《Always set the "applicationName" property when configuring ASP.NET 2.0 Membership and other Providers》) 下面的XML代码显示了如何定制SqlMembershipProvider设置。具体来说,属于黑体字的XML在<membership>元素里对设置进行定制。在其上还有一个<connectionString>节点,提供了连接到数据库的连接字符串(我们可以推算出来,该数据库构架是使用ASP.NET SQL注册工具添加的) : ![]() <configuration>
<connectionStrings> <add name="MyDB" connectionString="..." /> </connectionStrings> <system.web> ... authentication & authorization settings ... <membership defaultProvider="CustomizedProvider"> <providers> <add name="CustomizedProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="MyDB" applicationName="ScottsProject" minRequiredPasswordLength="5" minRequiredNonalphanumericCharacters="0" /> </providers> </membership> </system.web> </configuration> ASP.NET 2.0包含了一个内置的名为LocalSqlServer的连接字符串,它指向 App_Data文件夹里的ASPNETDB数据库.如果你希望使用默认的ASPNETDB数据库,并只改变一些属性.将connectionStringName配置为LocalSqlServer. 在<membership>元素里添加一个名为CustomizedProvider的新provider,并作为默认的membership provider.该自定义provider仍然使用SqlMembershipProvider ;将connectionStringName设置为MyDB(就是在<connectionStrings>节点指定的);将applicationName设置为ScottsProject;将minRequiredPasswordLength设置为5;将minRequiredNonalphanumericCharacters设置为0.我们只是对很少的属性进行自定义设置,全部属性清单见《<add> Element for Providers for Membership》。 Security Web控件的简单概述 Security Web控件为处理与用户帐户相关的的任务提供了一个用户界面.主要包括7种security user控件: Login控件——该控件呈现为标准的username/password登录界面.默认下,当用户点击"Login"按钮时,立即产生页面回传,控件调用Membershipclass类的VerifyUser(username, password)方法来对用户进行验证,如果验证通过则为用户创建一个authentication ticket,否则显示一个出错信息. 为LoginError event事件创建事件处理器,便于当用户为通过验证时自定义处理步骤;为Authenticate event事件创建事件处理器,以执行你个人的验证逻辑;Login控件包含了很多的属性供配置,以改变用户界面的外观.要完善控件,可以使用LayoutTemplate,下面的截屏为Login控件的默认用户界面: ![]() LoginView控件:有时我们希望根据访问者是匿名用户还是登录用户来显示不同的内容:比如,当一个匿名用户访问主页时你可能希望显示一个Login Web控件,而如果是一个登录用户的话,我们希望显示一个消息,比如:"Welcome back, username" ,再附加一个注销的链接. LoginView控件提供了2个模板:AnonymousTemplate 和 LoggedInTemplate模板.将显示给匿名用户的Web控件和HTML标记放在AnonymousTemplate模板里;而将显示给登录用户看的放在LoggedInTemplate模板;此外该控件还可以根据登录者角色的不同显示不同的界面. PasswordRecovery控件-允许用户重新获取现有的密码或将一个新密码传递到用户的电子邮件地址.如果密码在存储时经过哈希算法处理,那么“重新获取”密码实际上是创建一个新的随机的密码,再送回给你.如果密码是纯文本或经过加密处理,那么将把现有密码送回给用户. LoginStatus控件—如果是匿名用户,该控件将显示一个转到登录页面的连接;如果是登录用户将显示一个注销链接. LoginName控件——该控件仅仅将登录用户的username显示出来。另外,可以通过User.Identity.Name,编程访问用户的名称. CreateUserWizard控件——除了登录的页面外,我们还需要一个供访问者注册的页面。而CreateUserWizard控件就提供了创建用户账号所需的用户界面,当用户输入必需的数据并点"Create User"按钮时,将调用Membership class类的 CreateUser(...)方法.如果需要的话,可以用templates对CreateUserWizard控件进行定制.下面的截屏为运行中的CreateUserWizard控件. ![]() 同样可以对CreateUserWizard控件的布局和参数进行定制,详情见《Customizing the CreateUserWizard Control》(http://aspnet./articles/070506-1.aspx) ChangePassword控件——该控件允许用户改变其密码 所有的security Web控件在使用的时候可以不用写一行代码。比如你要创建一个登录页面,你只需要创建一个Login.aspx页面并拖一个Login控件到页面上。瞧,就这么简单,不用写一行代码。此外security Web控件包含了丰富的事件模型,你可以编程处理各种不同的事件.在本文的下载内容里包含了几个页面演示如何使用这些控件. 编程使用Membership System 可以通过Membership class类来使用membership system的函数,比如: GetAllUsers(), CreateUser(), DeleteUser()等等.你可以通过security Web控件在使用这些方法,也可以直接从ASP.NET页面调用这些方法.比如你可以创建一个页面来列出所有的用户,方法是将Membership.GetAllUsers()返回的结果绑定到一个GridView控件上。在本文下载内容里有几个编程使用Membership class类的示例. 结语: 由于很多网站需要用户账号支持,所以ASP.NET对该功能的支持是很有意义的.不过在ASP.NET 1.0版本里面,本目标只达到了一半。虽然基于窗口的验证提供了标准的手段包含一个authentication ticket来维系用户的登录状态,但无法存储用户帐户信息或创建必要的web页面(比如:登录、创建帐户等等)。在2.0版本,ASP.NET包含了membership服务和一些security Web控件.以弥补1.x版本的不足. 在本文,我们考察了membership system的注意目标,一个其中一个内置的membership providers——SqlMembershipProvider.该SqlMembershipProvider将用户帐户信息存储在一个SQL Server数据库,我们也可以通过Web.config文件对其进行用户定制。此外我们还总览了ASP.NET的security Web控件.它用来执行登录、创建用户帐户,以及其他与用户帐户相关的用户界面元素. 除了membership system外,ASP.NET 2.0还包含了roles 和 profile system.我们将在后面的文章探讨它们. 祝编程快乐! 考察Membership, Roles, 和Profile - Part 2 导言: 在Part 1我们已经提到Membership class类包含很多方法可以用来创建、删除、修改、检索、验证用户.由于每个开发者的需求不同,Membership class类被设计为使用provider模式。这就意味着membership framework在实际的执行过程中可以进行用户定制.ASP.NET包含有SqlMembershipProvider和ActiveDirectoryMembershipProvider,如果有必要的话,你可以构建自己的provider. 很多网站需要将用户划分为不同的角色.对于处于某种角色的用户,要指定其可以访问哪些页面、可以看到页面的哪些内容、页面某些区域的内容对用户是可编辑还是只读.使用ASP.NET 2.0的roles service的话,对用户进行角色划分、基于角色的功能和验证都比较简单了.与membership service类似,我们可以创建、删除role,对用户分配或移除角色;哪些用户属于哪个角色. 在本文,我们将考察ASP.NET 2.0的role service.我们最开始将看如何在网站里建立并配置roles service,以及如何基于认证规则来使用roles. 此外,我们看如何编程来处理roles service,以及如何使用LoginView Web控件,根据用户的角色显示信息. 先前准备 为使你的website支持角色,你的网站首先要有用户帐户,这些帐户要么 由SqlMembershipProvider存储在数据库里;要么存储在其它的数据库里. 而role只是一个概念,将role分给具体的用户. 设置你的Website支持Role 和membership service一样,roles service也是使用provider模式,只是ASP.NET 2.0有3个role providers: 1.SqlRoleProvider(默认的)—将role信息存储在一个SQL Server数据库里.如果你使用SqlMembershipProvider的话,也可以用它来处理roles.具体来说,roles service使用2个表:aspnet_Roles表用来为系统里的每一个role生成一条记录;另一个是aspnet_UsersInRoles表,该表将aspnet_Users表与aspnet_Roles表里的roles联系起来. 2.WindowsTokenRoleProvider—获得Windows用户的族群信息.如果你是由Windows authentication登录的授权用户,那么该provider允许你查看用户所在组的情况 3.AuthorizationStoreRoleProvider—由授权管理策略提供role信息,比如Active Directory. 本文将只对SqlRoleProvider provider进行考察,为了使用该provider,我们必须创建相应的数据库表.就像我们在Part 1探讨的那样,有2种方式: 1.通过ASP.NET SQL Server注册工具(aspnet_regsql.exe)——该工具允许你将必需的数据库表、视图、存储过程拷贝到指定的SQL Server 2000 或 SQL Server 2005数据库. 2.通过ASP.NET Website管理工具——当将认证类型选为"From the internet"时,该工具自动的调用aspnet_regsql.exe工具,在App_Data文件夹里的ASPNETDB.mdf数据库里创建相应的数据表. 假设你已经创建好了必需的数据库表,要激活role service的话,打开ASP.NET Website管理工具,点击“Security”标签,再点击"Enable roles"链接,如下所示: ![]() 在Web.config文件里添加如下的代码: <roleManager enabled="true" /> 如果你是使用的自己的数据库来存储SqlMembershipProvider 和 SqlRoleProvider providers的数据,你需要在<roleManager>元素里指定 一个provider,像下面这样: <configuration> <connectionStrings> <add name="MyDB" connectionString="..." /> </connectionStrings> <system.web> ... authentication & authorization settings ... <roleManager enabled="true" defaultProvider="CustomizedRoleProvider"> <providers> <add name="CustomizedRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="MyDB" /> </providers> </roleManager> </system.web> </configuration> 其中,<connectionString>设置为myDB,其指定的连接字符串连接的数据库里存储的是与membership 和 role有关的数据库表.(我们也可以在<roleManager> 和 <providers>元素里添加额外的特性,我们将在后面的文章探讨) 管理Roles role service提供了一个Roles class类,该类包含的方法可用于创建和删除角色,以及从角色里添加或移除用户.当你希望以编程的方式来完成这些事情的时候,这是比较有用的.不过我们通常以手工的形式来进行操作.我们可以用ASP.NET Website管理工具来完成,一旦一个角色被激活后,"Enable roles"链接就会变成这2个链接:"Disable roles"和"Create or Manage roles"。点击后一个链接的话,在接下来的界面里你可以添加新角色或管理、删除现有的角色. ![]() 点击Manage链接的话,你将看到某个角色下的一系列用户.类似的,退回到Security标签,选"Manage users"的话,你也可以看到一个用户所属的一系列角色. 基于Role的认证 在ASP.NET 1.x版本里,利用Web.config文件里的<authorization>元素,我们可以对基于URL的认证(URL-based authorization),进行user和role级的限制.具体来说,一个<authorization>元素包含多个<allow> 和 <deny>元素,以指定一个文件夹或URL的认证角色.默认情况下anyone可以访问一个URL,因此如果你要进行限制的话,你应该将<allow> 和 <deny> 元素合理的组合起来. 在2.0版本里,我们可以通过ASP.NET Website管理工具任意的进行设置.(当然,如果你愿意的话,也可以手工在Web.config文件添加<authorization>元素)。在管理工具的Security标签,点"Create access rules"链接. 这样在接下来的界面里你可以将authorization设置为users, user classes(*代表所有的users,?代表匿名users)或者roles(如果系统里有的话).选一个文件夹应用rules,指定user, user class,或 role,然后选择访问权限(Allow 或 Deny) 处理Roles Class类 通过Role class类可以编程的方式以多种方法访问role service,比如: .CreateRole(roleName):向系统添加新的角色 .DeleteRole(roleName):从系统删除角色 .AddUserToRole(userName, roleName):向特定角色添加特定的用户 .IsUserInRole(roleName) / IsUserInRole(userName, roleName):返回true 或 false.判断当前用户是否是某个特定角色的一员. .GetAllRoles():返回有关系统所有的角色的字符串数组 .GetRolesForUser() / GetRolesForUser(userName):返回一个字符串数组,要么是当前用户所属的所有角色,要么是某个指定用户所属的所有角色. 这些方法可以用来在页面展示系统里所有的角色,或某个用户所属的所有角色,或判断某个用户是否属于某个角色.本文结尾部分的示例有2个页面来演示如何使用Roles lass类:一个是UserList.aspx页面,它列出了系统的所有用户以及用户所属的角色。另一个是RoleList.aspx页面, 它列出了系统所有的角色以及每个角色所属的用户. 在LoginView控件里使用基于角色的代码 在前面我们总览了ASP.NET 2.0里的security Web控件.其中一个是LoginView控件,它有2个模板:AnonymousTemplate和LoggedInTemplate. 当一个匿名用户登录页面时,该控件将显示AnonymousTemplate模板里的内容;如果是注册用户登录的话,将显示LoggedInTemplate模板里的内容. LoginView控件也可以包含基于角色的模板,比如向控件添加一个 <RoleGroups>元素,再在里面放一个<asp:RoleGroup Roles="comma-delimited list of roles">模板,如下: <asp:LoginView ID="LoginView1" runat="server"> <AnonymousTemplate> OMG, you are, like so not logged in! </AnonymousTemplate> <RoleGroups> <asp:RoleGroup Roles="Developer"> <ContentTemplate> Welcome back! You are a Developer, I can tell by your svelte figure and impeccable social skills! </ContentTemplate> </asp:RoleGroup> <asp:RoleGroup Roles="Administrator"> <ContentTemplate> We all bow down to our system adminstrators! </ContentTemplate> </asp:RoleGroup> </RoleGroups> <LoggedInTemplate> You are logged in!! But, wait, you are not a member of any roles. </LoggedInTemplate> </asp:LoginView> 当访问者登录页面时,如果有对应其角色的模板,就显示该模板里的内容. 如果你以Developer角色登录的话,你就会看到Developer模板的内容,而不是LoggedInTemplate模板的内容.类似的,如果你属于Developer 和 Administrator角色, 你将会看到看到模板列表里第一个吻合的模板(就本例而言,为Developer) 结语: 就像我们在本文看到的那样,ASP.NET 2.0支持roles和membership services 。它们都使用provider model模式。都包含很多方法来执行相应的任务.都可以通过ASP.NET Website管理工具进行配置. 激活了roles功能后,我们一个通过编程或使用Website管理工具手工的创建角色或向角色赋值.ASP.NET允许基于角色的验证规则.所以你可以对各种文件和文件夹设置访问权限.最后,LoginView控件提供了基于角色的模板,那意味着可以根据角色的不同显示不同的内容. 祝编程快乐! 考察 ASP.NET 2.0的Membership, Roles, and Profile - Part 3 导言: 默认情况下,ASP.NET使用的membership 和 roles 分别使用的是SqlMembershipProvider 和 SqlRoleProvider.它们将membership和roles信息存储在一个SQL Server数据库里. 具体来说,信息存储在一个预先定义好了的表里,并可以通过一些预定义的存储过程来进行访问.为了在你的应用程序里通过默认的provider来使用membership 或 roles,我们需要将这些预定义的构造应用到你的应用程序里. 如果你没有数据库,还好ASP.NET可以在App_Data文件夹里创建一个SQL Server 2005 Express版本的数据库以包含必需的数据结构(schema).开发者经常要面对这种情况——手动将ASP.NET 2.0的 membership和roles特性添加到一个现有的数据模式(data model)。比如,你的应用程序目前还不支持membership 或 roles services,但是需要网站的某些部分对认证用户可见;或对属于某种角色的用户限制其功能.这时,我们一个通过the ASP.NET SQL Server注册工具(aspnet_regsql.exe)使一个现有的数据库支持membership 和 roles. 在本文,我们将考察如何用该工具对SqlMembershipProvider 好 SqlRoleProvider providers应用membership 和 roles services.该工具可以通过命令行或图形界面来运行.另外,我们还将看如何对App_Data文件夹里的现有的SQL Server 2005 Express数据库运用该工具. 如Part 1所述,使用ASP.NET Website管理工具(AWAT)可以在App_Data文件夹里创建一个名为ASPNETDB.MDF的数据库.有可能有这种网站,其有2个数据库,一个是ASPNETDB.MDF数据库,一个是应用程序自己使用的数据库.当涉及到与membership和roles有关的查询时,比如验证用户的身份,或创建一个新的用户帐户等,就使用ASPNETDB.MDF数据库;而网站页面依然使用原来的数据库.下面的图阐明了这种关系. ![]() 最理想的是将与membership相关的数据结构(schema),转移到应用程序现有的数据库,所有的查询都要使用现有的数据库.如下图: ![]() 不过,我们一定要这么做吗?为什么不能使用2个数据库,一个处理membership services另一个处理现有程序的工作?然而不管怎么样我依然建议将这2者合并起来. 打个比方,设想你有一个相薄程序,目前只有你才能添加相片。你可以运用ASP.NET 2.0的 membership功能,只允许认证用户才能访问该图片,另外,你还可以为你或你的配偶创建1到2个用户帐户。在这种情况,将membership schema添加到现有的数据库是比较理想的.另外,如果你允许访问者创建自己的帐户,并对图片发表评论的话,你可以在现有数据库创建一个Comments表,该表引用aspnet_Users表的一个外键,以便显示是那个人发表的评论. 将Membership相关的Schema应用到一个现有的数据库 要完成这个工作,使用ASP.NET SQL Server注册工具(aspnet_regsql.exe)是很容易办到的.在命令行里导航到%WINDOWS%\Microsoft.NET\Framework\v2.0.50727,再运行aspnet_regsql.exe这将开启ASP.NET SQL Server Setup Wizard向导,在向导里你要么为application services配置一个SQL Server数据库(也就是添加membership相关的schema),要么从数据库转移出schema,如下图: ![]() 在接下来的界面,你要选择一个数据库来添加或(转移入)与membership相关的schema.如下图: ![]() 指定了数据库后,点Next预览将要做的改动,预览完后点Next完成schema的配置。这将为membership providers添加必要的表、视图、存储过程. ASP.NET SQL Server注册工具所要做的是将%WINDOWS%\Microsoft.NET\Framework\v2.0.50727\InstallCommon.sql创建的脚本稍加修改后运行.而%WINDOWS%\Microsoft.NET\Framework\v2.0.50727\InstallCommon.sql的脚本将尝试连接名为aspnetdb的数据库.它将建立所有与membership相关的服务: Membership, Roles, Profile, Personalization等等.更多的脚本见%WINDOWS%\Microsoft.NET\Framework\v2.0.50727\目录. 脚本尝试连接具体的数据库(要么是aspnetdb数据库,要么是在向导里指定的数据库)。如果它不能在server's sysdatabases table表里找到该数据库的话, 那么就将根据指定的名称创建数据库,再添加各种表、视图、存储过程. 从命令行运行ASP.NET SQL Server注册工具 除了图形界面以外,我们也可以从命令行调用ASP.NET SQL Server注册工具。导航到 %WINDOWS%\Microsoft.NET\Framework\v2.0.50727,运行: -- To use Windows Authentication (i.e., a "trusted connection"), use: aspnet_regsql.exe -S <server> -E -d <database> -A all -- To use SQL Server credentials (a UserID and Password), use: aspnet_regsql.exe -S <server> -U <login id> -P <password> -d <database> -A all 其中-A all将安装与membership相关的所有服务。你也可以用-A list来安装某个具体的membership特性,比如-A mr将安装membership和roles.运行aspnet_regsql.exe -? 将查看所有选项的全部清单. 如果你的应用程序现有的数据库为App_Data文件夹里的一个SQL Server 2005 Express Edition版本的数据库.你也许想知道如何正确的通过ASP.NET SQL Server注册工具来访问它. 那么你如何处理server name, credentials,以及database name呢?首先声明我不是专家,下面的意见为我自己认识到的: .Server name为localhost\InstanceName——默认时SQL Server 2005 Express Edition的InstanceName为SQLExpress,因此使用的Server name就为localhost\SQLExpress. .SQL Server 2005 Express Edition默认安装为接受Windows Authentication.所以将authentication项设置为Windows Authentication. .database name有点棘手.就我所知,当在App_Data数据库里创建一个SQL Server 2005 Express Edition数据库时,那么数据库名为其完整的路径,比如如果数据库完整路径为C:\Home\Websites\PhotoAlbum\App_Data\Photos.mdf,那么其数据库名也为C:\Home\Websites\PhotoAlbum\App_Data\Photos.mdf. 不过我们需要将其添加为SQL Server 2005 Express Edition实例,以便于ASP.NET SQL Server注册工具引用. 对现有的SQL Server 2005 Express Edition数据库提供一个名称,使用免费下载的SQL Server 2005 Management Studio Express Edition来做是很容易的. 如果你的电脑里安装的是SQL Server 2005 Standard Edition或更高版本的话,那么你也已经装好了SQL Server 2005的Management Studio 。那么你就无法再安装Management Studio的Express Edition版本了. 如果你不能在连接屏幕通过:localhost\SQLExpress (或者你安装的其它目录)来连接到SQL Server 2005 Express Edition数据库实例的话, ![]() 那么在Management Studio里,连接到一个database server后,在 Databases文件夹上点击右键,选“Attach”,来分配数据库.你然后可以选择需要的数据库。该数据库的名称为初始(original)创建时的完整路径名.我说“初始”是因为如果你将该数据库文件从一个目录移动到另一个目录的话,它的名称依然是最初创建时的完整路径.要改变数据库的名称,可以点“Script” 按钮,再重命名。还一个方法,在Management Studio里,在该数据库上右键单击,选“Rename”. ![]() 完成添加后,我们就可以用ASP.NET SQL Server注册工具来引用它.如果你不想用Management Studio来进行添加,那么请参阅文章《Working With SQL Server 2005 Express Databases》(http:///sowblog/posts/5480.aspx) 让Membership Providers使用应用程序现有的数据库 当将与membership相关的schema转移到应用程序现有的数据库后,我们将从现有数据库进行membership相关的查询,而不是从ASPNETDB.MDF数据库.然而SQL Server Membership的几个provider——SqlMembershipProvider, SqlRoleProvider, SqlProfileProvider等都默认使用ASPNETDB.MDF数据库.为此,我们要在Web.config文件里添加我们自己的provider实例以使用现有的数据库,并作为默认的Membership providers. 就像在Part 1探讨的那样,我们可以在Web.config文件对provider信息进行配置,使membership providers和roles providers调用现有的数据库而不是ASPNETDB.MDF,如下: <configuration> <connectionStrings> <add name="MyDB" connectionString="..." /> </connectionStrings> <system.web> ... authentication & authorization settings ... <roleManager enabled="true" defaultProvider="CustomizedRoleProvider"> <providers> <add name="CustomizedRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="MyDB" /> </providers> </roleManager> <membership defaultProvider="CustomizedMembershipProvider"> <providers> <add name="CustomizedMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="MyDB" /> </providers> </membership> </system.web> </configuration> 以上设置为membership 和 roles systems创建了一个用户自定义providers,用到的连接字符串为“MyDB”,它指向了应用程序里现有的数据库. 结语: 当对程序启动membership支持后,我们需要将membership schema应用到一个现有的数据库上,该工作可以通过ASP.NET SQL Server注册工具来完成。完成之后,最后一步是对provider进行定制,以使用现有的数据库而不是默认的ASPNETDB.MDF 祝编程快乐! 考察 ASP.NET 2.0的Membership, Roles, and Profile - Part 4 导言: ASP.NET 2.0 的Membership class类有一个ValidateUser(userName, password)方法,返回一个布尔值。用于验证用户的登录信息是否有效.该方法被Login Web控件自动调用,当然如果需要的话也可以通过编程调用.在Membership system,有几种情况,用户无法通过验证: 1.username无效 2.username有效,但password无效 3.username和password都有效,但 .用户可能还未被核准(approved) .用户被锁定(locked out),原因可能是用户用无效密码登录了好几次( 默认为5次) 不幸的是,如果登录失败,ValidateUser(userName, password)方法只会返回一个False,而不包含为什么登录失败的具体原因.对Login控件而言,当ValidateUser(userName, password)方法返回False的同时,默认还会显示一个消息:"Your login attempt was not successful. Please try again." 如果是这种情况,用户被锁定(locked out)或帐户还未被核准——此时他们的username和password都没问题,显示这样的消息就让用户感到困惑了.在本文,我们将在登录过程中添加额外的反馈信息,以避免这种尴尬的情况. 审核和锁定(Locked Out)用户帐户 ASP.NET 2.0 Membership system里的用户帐户,我们可以利用Membership 和 MembershipUser classes类以编程的方式访问并进行修改.Membership包含一系列的方法以返回某一个或所有用户的信息、更新某个用户的信息等;而MembershipUser class类包含的属性描述了某个用户的具体情况(UserName, Email, LastLoggedOnDate等等). Membership system可以将用户帐户标记为inactive(未审批)和locked out.当创建一个新账号时,默认是被审批的.然后在某些情况下,你可能希望让管理员手动批准新帐户,或者是其它自动方式来审批(比如,点击一个确认连接发送邮件).不管是哪种方式,刚创建的用户都会被标记为inactive.这样,该用户就无法登录网站,因为ValidateUser(userName, password)总是返回False,无论用户名和密码是否正确. 由于验证过程是通过一个基于窗体的构架(forms-based scheme),只是简单的通过一个HTTP request来发送用户认证(credential).这样,某个攻击者便可以尝试破解某个用户帐户,方法是编写脚本遍历一个通用密码字典(a dictionary of common passwords),对某个特定的帐户,用字典里的所有密码尝试进行登录.为阻止这种攻击,如果在特定时间段内用无效密码登录了特定次数后,Membership system自动将某个用户锁定.默认设置为在10分钟内(a ten minute window)无效登录5次.当然,我们可以在Web.config文件里对其进行定制.和未审批用户一样,被锁定的用户同样不能登录网站,无论其用户名和密码是否正确.为了对用户进行解锁,需要调用MembershipUser class类的UnlockUser()方法.当从Login Web控件登录失败时,我们可以对登录页面进行定制以显示更多恰当的消息. 登录失败时显示更有价值的消息 当用户用Login Web控件进行登录时就会触发其LoginError事件.该事件不会传递指明登录失败的任何信息.不过,我们可以通过Login控件的UserName 和 Password属性来获取该用户的username 和 password.我们可以调用Membership.GetUser(userName)方法来获取用户帐户的信息. 该方法返回一个MembershipUser对象,我们可以检查其IsApproved 和 IsLockedOut属性来判定为何用户的认证被认为是无效的. 下面的代码显示了如何来实现它.最终的帮助信息显示在一个名为LoginErrorDetails的Label Web控件里.你在本文的下载代码里可看到Login页面的完整代码和声明. Protected Sub Login1_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoginError 'There was a problem logging in the user 'See if this user exists in the database Dim userInfo As MembershipUser = Membership.GetUser(Login1.UserName) If userInfo Is Nothing Then 'The user entered an invalid username... LoginErrorDetails.Text = "There is no user in the database with the username " & Login1.UserName Else 'See if the user is locked out or not approved If Not userInfo.IsApproved Then LoginErrorDetails.Text = "Your account has not yet been approved by the site's administrators. Please try again later..." ElseIf userInfo.IsLockedOut Then LoginErrorDetails.Text = "Your account has been locked out because of a maximum number of incorrect login attempts. You will NOT be able to login until you contact a site administrator and have your account unlocked." Else 'The password was incorrect (don't show anything, the Login control already describes the problem) LoginErrorDetails.Text = String.Empty End If End If End Sub 使用上面的代码,用户登录失败后将看到更有意义的消息.下面的截屏显示的是以Bruce(其帐户已被锁定)和Alfred(其帐户还未被审批)的名字登录的情形.没有上述的事件处理器,他们都只能看到标准的"Your login attempt was not successful. Please try again." 提示消息.很明显,他们将感到很迷惑,无法意识到其帐户已经被锁定或未被审批. ![]() ![]() 由于ASP.NET 2.0 Membership system可以锁定用户帐户并不再接受登录.如此情况,可提供一个快速反馈,看哪些用户被锁定,哪些用户未被审批.要捕获这些信息,我在membership数据库里创建了一个InvalidCredentialsLog表,其构架如下: ![]() Membership system包含一个ApplicationID,它允许多个应用程序在一个数据库里存储各自的用户帐户.理想情况下,该表应包含ApplicationID,并只报告当前应用程序的那些无效认证(假定你用一个单一的membership store来应对多个应用程序).我将其作为练习留给读者. 接下来,我将创建一个存储过程——InvalidCredentialsLog_Insert,它存储用户的username, password,和IP address.它检查当前username是否与aspnet_Users表里的用户匹配,若是,则提取用户的IsApproved 和 IsLockedOut列的值,然后添加到InvalidCredentialsLog_Insert表. 当用户无效登录时,需要调用该存储过程,并传入用户信息.为此,我创建了一个ID为InvalidCredentialsLogDataSource的SqlDataSource控件,设置其调用该存储过程,然后我们对Login Web控件的LoginError事件处理器进行扩充,为该存储过程设置参数并调用它: Protected Sub Login1_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoginError 'Set the parameters for InvalidCredentialsLogDataSource InvalidCredentialsLogDataSource.InsertParameters("ApplicationName").DefaultValue = Membership.ApplicationName InvalidCredentialsLogDataSource.InsertParameters("UserName").DefaultValue = Login1.UserName InvalidCredentialsLogDataSource.InsertParameters("IPAddress").DefaultValue = Request.UserHostAddress 'The password is only supplied if the user enters an invalid username or invalid password - set it to Nothing, by default InvalidCredentialsLogDataSource.InsertParameters("Password").DefaultValue = Nothing 'There was a problem logging in the user 'See if this user exists in the database Dim userInfo As MembershipUser = Membership.GetUser(Login1.UserName) If userInfo Is Nothing Then 'The user entered an invalid username... LoginErrorDetails.Text = "There is no user in the database with the username " & Login1.UserName 'The password is only supplied if the user enters an invalid username or invalid password InvalidCredentialsLogDataSource.InsertParameters("Password").DefaultValue = Login1.Password Else 'See if the user is locked out or not approved If Not userInfo.IsApproved Then LoginErrorDetails.Text = "Your account has not yet been approved by the site's administrators. Please try again later..." ElseIf userInfo.IsLockedOut Then LoginErrorDetails.Text = "Your account has been locked out because of a maximum number of incorrect login attempts. You will NOT be able to login until you contact a site administrator and have your account unlocked." Else 'The password was incorrect (don't show anything, the Login control already describes the problem) LoginErrorDetails.Text = String.Empty 'The password is only supplied if the user enters an invalid username or invalid password InvalidCredentialsLogDataSource.InsertParameters("Password").DefaultValue = Login1.Password End If End If 'Add a new record to the InvalidCredentialsLog table InvalidCredentialsLogDataSource.Insert() End Sub 此外,我还构建了一个报告页面,在一个可分页、排序的GridView控件.如下所示,该页面也包含在下载内容里 ![]() 就一般的网站而言,该InvalidCredentialsLog表可能会很大。我们可以采取一些措施来将超过某个时间的老的记录删除(比如3个月前的记录)。上面显示出来的报告很简单,对大数据的结果也没有进行优化处理.它使用默认的分页,从数据库将每个页面的数据都检索出来。如果想自定义分页,仅仅检索当前页面所需要的记录,请参阅文章《Custom Paging in ASP.NET 2.0 with SQL Server 2005》(http://aspnet./articles/031506-1.aspx) 结语: 本文,我们看如何对登录过程进行优化,当被锁定或未为被审批的用户登录时显示更有意义的信息.这都要感谢Membership API,它可以通过编程、显式地(通过数据源控件)、或Web控件(比如Login Web控件)来访问.在下载代码里有一个Admin页面,它用一个GridView控件将系统的所有用户列出来,允许用户查看其审批状态、将锁定的用户解锁、查看某人用户是否是Administrator角色(只有Administrator角色才能查看与管理相关的页面) 祝编程快乐! 考察ASP.NET 2.0中的Membership, Roles, and Profile - Part 5 导言: 我们知道ASP.NET 2.0通过membership, roles,profile systems来创建和管理用户帐户。要为用户提供登录页面的话,我们只需要拖一个Login Web控件到页面即可.但如果我们想做一些用户定制呢?我们可以重新配置Login控件,再另外添加一些内容;或者出除了用户名和密码外,我们还希望用户提供email地址等,或者包含一个CAPTCHA(一些box,其text的背景为图片).可以通过多种方式来对Login Web控件进行定制.比如,是否显示"Remember me next time", 颜色、字体等设置;我们还可以将控件转换成一个模板(template);我们还可以为Authenticate event事件创建处理器,自定义认证逻辑,甚至使用CAPTCHA作为认证的一部分。 本文,我们将考察如何通过Login控件的属性、通过模板来对其进行定制,通过Authentication事件处理器定制认证逻辑. 通过属性定制Login控件 首先我们要创建一个登录页面,如果用户尝试访问其未被授权的页面或点击LoginStatus控件的Login按钮时就会自动的重新定位到该页面。默认情况下,该页面必须命名为Login.aspx,并放在根目录下.当然如果你喜欢的话也可以使用其它的登录URL,但是你需要在Web.config文件里的<authentication>节点里更新<forms>元素: <?xml version="1.0"?> <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.web> ... <authentication mode="Forms"> <forms loginUrl="LoginPath" /> </authentication> ... </system.web> </configuration> 从工具栏拖该Login控件到我们创建的登录页面上,如下图所示,Login控件默认包含如下的内容: . 一个User Name TextBox . 一个Password TextBox . 一个"Remember me next time" CheckBox . 一个"Log In" Button 此外,Login控件还有RequiredFieldValidators控件,以确保用户输入的User Name和Password不为空。另外,还有一个Literal控件用来显示Login控件的FailureText属性的值. ![]() 可以通过各种与style相关的属性来美好Login控件的外观。比如Font, BackColor, Width, Height等等.另外还可以通过TextBoxStyle, CheckBoxStyle,和LoginButtonStyle来设置Login控件内部的TextBoxes, CheckBox,和Button Web控件. 我们还可以改变其属性。比如,想将 "User Name:"文本换成别的吗?改动UserNameLabelText属性即可;想在添加说明吗?在InstructionText属性设置即可;通过LoginButtonText属性改变"Log In" Button的文本.通过HelpPageText 和 HelpPageUrl属性来添加一个定位到帮助页面的链接.而Orientation属性决定了Login控件如何布局.下图以及下载内容包含的Login控件演示了如何改变相关属性的值来改变其外观。 ![]() 以上只是进行了一定程度的定制,要想进行更大程度的定制,我们必须将其转变成一个模板。为此,切换到设计模式,在智能标签里点"Convert to Template" 项。这将在Login控件的声明代码里添加一个 <LayoutTemplate>,包含一个HTML <table> ,其构造复制了默认的Login控件的布局.你想怎么调整布局都行,包括添加额外的内容和Web控件. 有一点很重要要记住,在模板里的某些登录控件必须保留其原有的ID值。具体来说,<LayoutTemplate>里有2个控件的ID值为UserName和 Password,它们构成了ITextControl界面.另外,你可以使用你个人的自定义服务器控件或User Control,只要它们也可以执行这种界面.另外,你需要一个Button, LinkButton, 或ImageButton,其CommandName要设置为Login控件的LoginButtonCommandName静态域(默认为"Login"),但其ID却可以是任意值.Login控件转变成模板时自动生成的其它控件都是可选的. 下面的声明代码是我对Login控件的<LayoutTemplate>调整后的布局: <asp:Login ID="Login1" runat="server" BackColor="#F7F7DE" BorderColor="#CCCC99" BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" Font-Size="10pt" RememberMeSet="True"> <TitleTextStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" /> <LayoutTemplate> <table border="0" cellpadding="1" cellspacing="0" style="border-collapse: collapse"> <tr> <td align="center" rowspan="3" style="padding:15px; font-weight: bold; color: white; background-color: #6b696b"> Log In </td> <td style="width:8px;"> </td> <td align="right"> <asp:Label ID="UserNameLabel" runat="server" AssociatedControlID="UserName">User Name:</asp:Label></td> <td> <asp:TextBox ID="UserName" runat="server" TabIndex="1"></asp:TextBox> <asp:RequiredFieldValidator ID="UserNameRequired" runat="server" ControlToValidate="UserName" ErrorMessage="User Name is required." ToolTip="User Name is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator> </td> <td align="center" rowspan="3" style="padding:15px;"> <asp:Button ID="LoginButton" runat="server" CommandName="Login" Text="Log In" ValidationGroup="Login1" TabIndex="4" /> </td> </tr> <tr> <td style="width:8px;"> </td> <td align="right"> <asp:Label ID="PasswordLabel" runat="server" AssociatedControlID="Password">Password:</asp:Label></td> <td> <asp:TextBox ID="Password" runat="server" TextMode="Password" TabIndex="2"></asp:TextBox> <asp:RequiredFieldValidator ID="PasswordRequired" runat="server" ControlToValidate="Password" ErrorMessage="Password is required." ToolTip="Password is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator> </td> </tr> <tr> <td style="width:8px;"> </td> <td colspan="2"> <asp:CheckBox ID="RememberMe" runat="server" Text="Remember me next time." TabIndex="3" /> </td> </tr> </table> <asp:ValidationSummary ID="ValidationSummary1" runat="server" ShowMessageBox="True" ValidationGroup="Login1" ShowSummary="False" /> </LayoutTemplate> </asp:Login> 心细的读者可能已经注意到上面的布局忽略了FailureText Literal控件,该控件在用户登录失败时显示Login控件FailureText属性的值。我决定在一个客户端alert box里显示该文本。为此,我将为Login控件的 LoginError事件创建处理器,编写必要的JavaScript来展示该alert box。(在Part 4我们探讨过该事件) Protected Sub Login1_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoginError 'Display the failure message in a client-side alert box ClientScript.RegisterStartupScript(Me.GetType(), "LoginError", _ String.Format("alert('{0}');", Login1.FailureText.Replace("'", "\'")), True) End Sub 最后,请注意<table>标签后的ValidationSummary控件的内容,我将ShowSummary 和 ShowMessageBox属性做上述设置后,一旦输入有误(也就是说没有输入username 或password)时将显示一个客户端的alert box.下面的一个图,alert box显示用户必须输入username;而第二个图片,用户尝试登录,但输入有误. ![]() ![]() 定制认证逻辑 当用户输入完并点击"Log In"按钮时,紧接着发生如下的步骤: 1.发生页面回传 2.Login控件的LoggingIn事件触发 3.Login控件的Authenticate事件触发;它调用一个事件处理器以执行必要的逻辑,判断用户认证是否有效,若有 效则该事件处理器将传入的AuthenticateEventArgs对象的Authenticated属性设为True. 4.如果用户认证失败将引发LoginError事件,反之将引发LoggedIn事件 默认,Login控件在内部处理Authenticate事件,通过调用Membership class类的ValidateUser(username, password)方法来进行验证。然而,我们可以对Login控件使用的验证逻辑进行定制,我们可以自己创建该事件的事件处理器,如果通过验证就将e.Authenticated属性设为True;而未通过就设为False. 本例,我们不仅要验证username 和 password,而且还有其email地址,以及一个CAPTCHA.我们知道CAPTCHA是一种人可以感知(而机器人程序无法感知)的技术.CAPTCHA通常为在一个图片上显示一些扭曲的文本,让用户输入这些文本.计算机程序不能分析该图片以及破解其中的文本,而只有人才可以轻松的做到。我用到的该CAPTCHA是eff Atwood的免费且优秀ASP.NET CAPTCHA server control控件. 要创建一个用户自定义验证逻辑示例,我们先将一个标准的Login控件转变为一个模板,并添加一个名为Email的TextBox(附带一个RequiredFieldValidator控件),以及一个Jeff的CAPTCHA控件: <asp:Login ID="Login1" ...> <LayoutTemplate> <table border="0" cellpadding="1" cellspacing="0" style="border-collapse: collapse"> <tr> <td align="center" rowspan="5" style="padding:15px; font-weight: bold; color: white; background-color: #6b696b"> Log In </td> <td style="width:8px;"> </td> <td align="right"> <asp:Label Font-Bold="True" ID="UserNameLabel" runat="server" AssociatedControlID="UserName">User Name:</asp:Label></td> <td> <asp:TextBox ID="UserName" runat="server" TabIndex="1"></asp:TextBox> <asp:RequiredFieldValidator ID="UserNameRequired" runat="server" ControlToValidate="UserName" ErrorMessage="User Name is required." ToolTip="User Name is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator> </td> <td align="center" rowspan="5" style="padding:15px;"> <asp:Button ID="LoginButton" runat="server" CommandName="Login" Text="Log In" ValidationGroup="Login1" TabIndex="5" /> </td> </tr> <tr> <td style="width:8px;"> </td> <td align="right"> <asp:Label Font-Bold="True" ID="PasswordLabel" runat="server" AssociatedControlID="Password">Password:</asp:Label></td> <td> <asp:TextBox ID="Password" runat="server" TextMode="Password" TabIndex="2"></asp:TextBox> <asp:RequiredFieldValidator ID="PasswordRequired" runat="server" ControlToValidate="Password" ErrorMessage="Password is required." ToolTip="Password is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator> </td> </tr> <tr> <td style="width:8px;"> </td> <td align="right"> <asp:Label Font-Bold="True" ID="EmailLabel" runat="server" AssociatedControlID="Email">Email:</asp:Label></td> <td> <asp:TextBox ID="Email" runat="server" TabIndex="3"></asp:TextBox> <asp:RequiredFieldValidator ID="EmailRequired" runat="server" ControlToValidate="Email" ErrorMessage="Email is required." ToolTip="Email is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator> </td> </tr> <tr> <td style="width:8px;"> </td> <td align="right"> <b>Verification:</b> </td> <td> <cc1:CaptchaControl id="CAPTCHA" runat="server" ShowSubmitButton="False" TabIndex="3" LayoutStyle="Vertical"></cc1:CaptchaControl> </td> </tr> <tr> <td style="width:8px;"> </td> <td colspan="2"> <asp:CheckBox ID="RememberMe" runat="server" Text="Remember me next time." TabIndex="4" /> </td> </tr> </table> <asp:ValidationSummary ID="ValidationSummary1" runat="server" ShowMessageBox="True" ValidationGroup="Login1" ShowSummary="False" /> </LayoutTemplate> </asp:Login> 接下来,我们将为Authenticate事件创建一个事件处理器.我们先检查CAPTCHA是否有效,再用Membership.ValidateUser方法来确保用户的username 和 password是有效的;再调用Membership.GetUser(username)方法,将Email属性与用户提供的做对比;如果有任何一项出错,则将 e.Authenticated设为False,反之则设为True. 我对Login控件的FailureText属性进行了更新,显示用户无法登录的更具体的信息. 注意我们在<LayoutTemplate>里用LoginControl.FindControl("ID")方法来访问控件(比如Email TextBox 或 CAPTCHA控件). Protected Sub Login1_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles Login1.Authenticate 'We need to determine if the user is authenticated and set e.Authenticated accordingly 'Get the values entered by the user Dim loginUsername As String = Login1.UserName Dim loginPassword As String = Login1.Password 'To get a custom Web control, must use FindControl Dim loginEmail As String = CType(Login1.FindControl("Email"), TextBox).Text Dim loginCAPTCHA As WebControlCaptcha.CaptchaControl = CType(Login1.FindControl("CAPTCHA"), WebControlCaptcha.CaptchaControl) 'First, check if CAPTCHA matches up If Not loginCAPTCHA.UserValidated Then 'CAPTCHA invalid Login1.FailureText = "The code you entered did not match up with the image provided; please try again with this new image." e.Authenticated = False Else 'Next, determine if the user's username/password are valid If Membership.ValidateUser(loginUsername, loginPassword) Then 'See if email is valid Dim userInfo As MembershipUser = Membership.GetUser(loginUsername) If String.Compare(userInfo.Email, loginEmail, True) <> 0 Then 'Email doesn't match up e.Authenticated = False Login1.FailureText = "The email address you provided is not the email address on file." Else 'Only set e.Authenticated to True if ALL checks pass e.Authenticated = True End If Else e.Authenticated = False Login1.FailureText = "Your username and/or password are invalid." End If End If End Sub 做完这些修改后,该Login控件现在包含了用户的email以及一个CAPTCHA,如下所示,只有当用户提供正确的username, password, email address,CAPTCHA后才能正确登录. ![]() 结语: 本文,我们看到了如何通过改变Login控件的配置或将其转变为一个模板,来定制其外观和布局.当转变为模板后我们可以添加额外的Web控件,比如一个CAPTCHA.再为Login控件的Authenticate事件创建一个事件处理器,我们可以自定义验证逻辑. 祝编程快乐! 附录: 名词解释:CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart ,全自动区分计算机和人类的图灵测试) CAPTCHA ——用以区分计算机和人类,在人机差别非常小的网络上非常有效。它会生成一个随机的图片显示给用户。这个图片含有一个不容易被计算机识别(OCR)的字符串,同时这个字符串在页面的代码及其他计算机可以获取的地方被使用。如果表单提交的时候并不含有正确的字符串,系统就能够确信输入人员录入错误或者不是一个真正的人在进行录入。 沿着上述思路发展,除了图象外,目前又出现了语音形式的验证方法。 考察ASP.NET 2.0的Membership, Roles,Profile - Part 6 导言: 除了用户帐户的username, passsword, email, security question和 answer等,在实际的程序中我们还可能添加额外的信息,比如我们可能需要用户指定一个签名、主页URL、以及IM address等. 使用Membership model,有2种方法来将用户帐户与额外的信息联系起来.第一种,具有最大的灵活性也要做最多的准备工作——为额外信息创建自定义数据存储.如果你使用的是SqlMembershipProvider,那就意味着要创建一个额外的表,将aspnet_Users表的UserId值作为主键.以一个在线messageboard为例,该表可为forums_UserProfile,具有的列为UserId(将aspnet_Users表的UserId作为主键和外键)、HomepageUrl、 Signature、 IMAddress. 另外,我们还可以使用ASP.NET 2.0 Profile system来存储用户的具体信息.Profile system允许定义与用户相关的属性。一旦定义后,开发者就可以通过编程来读取和访问这些属性的值.与Membership、Roles类似,Profile system也是基于provider model,默认情况下,.NET Framework有一个SqlProfileProvider class类,它使用一个SQL Server数据库表(aspnet_Profile)来作为其存储备份(backing store). 在本文,我们将考察Profile system——如何定义用户属性以及如何从ASP.NET页面来编程访问它们。同时我们还将看到.NET 2.0里的SqlProfileProvide使用自定义的profile provider。 概述Profile System ASP.NET 2.0里面的Membership system设计来创建处理用户帐户的标准API.虽然Membership system有与用户相关的核心属性—username, password, email address等,但是我们还经常需要获取每个用户的其它信息.而且每个程序之间的额外信息还彼此不同. 为此,微软创建Profile system来处理这些额外的用户属性.Profile system允许将这些用户属性定义在Web.config文件并将其值存储在某些数据存储里.默认的Profile provider - SqlProfileProvider,将这些属性值存储在一个叫aspnet_Profile的SQL Server数据库表里. 当使用Profile system时,我们要记住其唯一的目的是作为一种定义一系列用户属性的方法.再通过某个具体的provider,将这些属性值存储在某些backing store里. 定义用户属性 用Profile system,我们必须要在Web.config文件里定义清楚.对每个属性我们要指定其name, 数据类型,以及数据应如何序列化(serialized).如下为4个serialization选项: .ProviderSpecific(默认)—Profile provider要决定如何序列化属性值. .String—属性值将转换成一个字符串形式 .Xml—属性值将转换成一个XML形式 .Binary—属性值将转换成一个二进制形式 你选择哪种serialization方法取决于变量的类型.如果允许将属性值转化为一个String,那么我们就要让provider将数据序列化为一个String;对XML和Binary类型,以此类推. 定义在Profile system的属性可为简单的标量类型(scalar),进而在聚合(grouped into)成复杂的类型,或已存在的复杂类型(例如一个类)。每个Profile属性的names, types,和serialization都定义在<profile> 元素的<properties>节点.比如,假象我们要用户指定一个主页的URL,我们可以添加如下的属性: <?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> ... <profile defaultProvider="CustomProfileProvider" enabled="true"> <providers> ... </providers> <!-- Define the properties for Profile... --> <properties> <add name="HomepageUrl" type="String" serializeAs="String" /> ... </properties> </profile> </system.web> </configuration> <add>元素添加了一个名为HomepageUrl,类型为String,serialized为String的属性。除此之外我们还可以在<add>元素添加额外的属性. 标量类型的属性可以通过使用<group>元素进行聚合。比如,除了HomepageUrl属性外,我们还想获取用户的实际位置、生日、选用的编程语言等.这些信息可以分别为3个标量值,也可以进行聚合.如下的代码显示如何将这3个属性值聚合为一个名为Bio的组. <profile defaultProvider="CustomProfileProvider" enabled="true"> <providers> ... </providers> <!-- Define the properties for Profile... --> <properties> <add name="HomepageUrl" type="String" serializeAs="String" /> <group name="Bio"> <add name="BirthDate" type="DateTime" serializeAs="Xml" /> <add name="Location" type="String" /> <add name="ProgrammingLanguageOfChoice" type="ProgrammingLanguages" /> </group> ... </properties> </profile> 上述代码里,BirthDate属性(类型为DateTime)被选作序列化为XML,而Location 和 ProgrammingLanguageOfChoice属性未指定serializeAs属性,那意味着Profile provider将自行决定如何序列化这2个属性.注意ProgrammingLanguageOfChoice属性的类型,该类型是我们在App_Code文件夹里创建的。代码如下: Public Enum ProgrammingLanguages As Integer NoneSelected = 0 VB = 1 CSharp = 2 JSharp = 3 End Enum 此外,Profile属性的类型还可以为一个自定义类,在本文下载内容里,你将发现一个很简单的Address class类(也是在App_Code文件夹): <Serializable()> _ Public Class Address Public Address1 As String Public Address2 As String Public City As String Public State As String Public Zip As Integer End Class 定义了上述类后,我们可以添加一个Address类型的属性,如下: <profile defaultProvider="CustomProfileProvider" enabled="true"> <providers> ... </providers> <!-- Define the properties for Profile... --> <properties> <add name="HomepageUrl" type="String" serializeAs="String" /> <group name="Bio"> <add name="BirthDate" type="DateTime" serializeAs="Xml" /> <add name="Location" type="String" /> <add name="ProgrammingLanguageOfChoice" type="ProgrammingLanguages" /> </group> <add name="BillingAddress" type="Address" serializeAs="Xml" /> <add name="ShippingAddress" type="Address" serializeAs="String" /> </properties> </profile> 上述代码里,BillingAddress 和 ShippingAddress属性都为Address类型. 在代码里处理Profile属性 完成Profile属性的定义后,ASP.NET引擎自动地创建一个ProfileCommon class类,该类包含的属性与我们在Web.config文件里定义的属性相匹配.当我们在Web.config文件里修改这些属性后,该类将重新创建并自动处理当前登录用户的profile.(你也可以设置profile对匿名用户的支持,不过这不是本文的主题,我们将在后面的文章探讨). 可以在一个ASP.NET页面里通过HttpContext对象的Profile属性来访问ProfileCommon class类。比如,要在一个ASP.NET页面里读取当前登录用户的HomepageUrl属性,仅仅只需要使用Profile.HomepageUrl即可. 实际上,只要你输入Profile再键入一个“.”智能感知功能将带出各种属性。酷吗?实际上聚合功能同样工作地很好,输入Profile再键入一个“.”,你将看到其中一个属性为Bio.输入Bio再键入一个“.”,那3个标量属性就出现在智能感知界面里(BirthDate, Location, and ProgrammingLanguageOfChoice). ![]() 本文下载代码里有3个页面值得考察,第一个页面为/UsersOnly/Default.aspx页面,显示了当前登录用户的Profile信息.第二个页面为/UsersOnly/UpdateProfile.aspx,允许用户更改其Profile数据.第三个页面演示了如何使用Profile属性的GetProfile("username")方法来获取其它,非当前登录用户的Profile数据.页面/UserList.aspx列出了系统里的所有用户的membership信息(username, email等),以及HomepageUrl属性值. 指定一个Profile Provider 如果你没有显式地指定一个Profile provider,那么默认使用的是SqlProfileProvider,它将用户的property值存储在aspnet_Profile表里,但效率比较低,该表的构架如下: ![]() 对于给定的记录,PropertyNames列包含的是每个Profile属性的名称,根据其序列化形式的不同,将其值存储在PropertyValuesString 或 PropertyValuesBinary列.如果是以String或 XML形式则存储在PropertyValuesString列;如果是以Binary形式则在PropertyValuesBinary列. 就我们定义的Profile属性而言,该某个用户的PropertyNames列的值可能为: Bio.ProgrammingLanguageOfChoice:S:0:92:HomepageUrl:S:92:30:ShippingAddress:B:0:212:... 如你所见,每个property name的形式为PropertyName:B|S:StartIndex:Length,其中B 或 S指出了该属性的值是存储在PropertyValuesString(S)列 还是在 PropertyValuesBinary(B)列的.比如,Bio.ProgrammingLanguageOfChoice属性的值存储在PropertyValuesString (S)列,起始位置为0,长度为92.而HomepageUrl属性的值也存储在PropertyValuesString (S)列,起始位置为92,长度为30. 查看PropertyValuesString列,我们看到: <?xml version="1.0" encoding="utf-16"?><ProgrammingLanguages>CSharp</ProgrammingLanguages>http://www.... ProgrammingLanguageOfChoice的值已经被序列化为XML形式,开始位置为0,长度为92;同样,HomepageUrl属性开始位置为92,长度为30.(而ShippingAddress属性为Binary形式,我们可以在PropertyValuesBinary列里找到其值,开始位置为0.长度为212) SqlProfileProvider自动的将Profile属性迁移到数据库.而SqlProfileProvider用到的表和存储过程是在贯彻SqlMembershipProvider时自动创建的(见Part 1). SqlProfileProvider的局限性 由于SqlProfileProvider将所有的属性值压缩到一行的1到2列,每次对任何一个属性值的读取都要触动所有的属性值;另外数据的格式也使的我们很难查询和返回这些属性信息。假如我们想知道有多少用户使用C#(该信息存储在PropertyValuesString列),我们首先需要将其数据格式转换成正常的数据结构. 作为SqlProfileProvider的替代,我们可以使用微软雇员Hao Kung开发的免费的Table Profile Provider(http://www./downloads/sandbox/table-profile-provider-samples/)。该provider将每个Profile属性存储在各自的列里。另外,我们开可以创建自己的Profile provider. 结语: 除了username, password, email address这些基本的用户信息外,如果你还想获取更多的用户信息,你要么使用自定义的解决方案(创建你自己的数据存储,写代码来对数据存储进行数据读写),要么使用Profile system. 它允许页面开发者在Web.config文件里定义一系列的用户属性,这些信息自动的转换为一个类(ProfileCommon),并可以通过HttpContext class类的Profile属性来进行访问. ASP.NET 2.0默认的Profile provider为SqlProfileProvider,它将Profile属性值存储在一个SQL Server数据库的aspnet_Profile表里.我们只需要通过Profile属性来读写用户的Profile数据. 祝编程快乐! 考察ASP.NET 2.0的Membership, Roles, and Profile - Part 7 导言: 在前6章我们考察了.NET Framework包括的那些provider——比如SqlMembershipProvider, SqlRoleProvider, SqlProfileProvider等.实际上 我们可以下载这些provider的源代码,以及一个Provider工具包来创建我们自己的provider(http://msdn2.microsoft.com/en- us//aa336558.aspx),而Scott Guthrie在他的博客文章《ASP.NET 2.0 Membership, Roles, Forms Authentication, and Security Resources》(http://weblogs./scottgu/archive/2006/02/24/ASP.NET-2.0-Membership_2C00_-Roles_2C00_-Forms- Authentication_2C00_-and-Security-Resources-.aspx)里列出了很多自定义的Membership, Roles, and Profile provider. 如果这些内置的provider无法满足你的需要,你可以创建自己的provider.本文,我们将探讨创建和使用一个自定义provider,然后用一个简单 的自定义provider将profile信息存储到一个XML文件里. Profile System的职责 像在Part 6探讨的一样,Profile System允许用户在Web.config文件定义一系列的 "profile properties",然后在某些存储备份(backing store)里保存和读取这些属性值. 如果这些内置Profile providers不能满足需要,你可以创建你自己的provider。创建一个用户定制provider——不管是用于Profile, Membership, Roles, ASP.NET site map,或是其他的什么子系统,都有遵循如下的步骤: 1.创建用户定制provider——创建源自ProfileProvider class类的一个类. 2.设置应用程序使用用户定制provide——我们需要在Web.config文件里指定Profile system使用我们的定制provid而不是默认的 SqlProfileProvider。 微软的Provider Toolkit web page页面里有关于学习创建用户定制provider的信息.对Profile system而言,有2个文件值得阅读: 《Introduction to Profile Providers》——它介绍了Profile system的概念以及职责;而《ASP.NET 2.0's Profile Providers》——它更 直观的介绍了 in ASP.NET 2.0里的SqlProfileProvider是如何贯彻的. 本文的剩下部分我们将探讨我们自定义的XmlProfileProvider——将用户信息装入App_Data文件夹里的一个XML文件,完整代码(以及简单的测 试程序)在本文的下载代码里. 设计XmlProfileProvider 默认,Membership, Roles,Profile system的provider都被设计为多个应用程序存储信息.明显的例子就是aspnet_Applications表,每一个应用程序实例对应一条记录.系统里的每一个用户通过aspnet_Users表里的ApplicationId这个外键与某个应用程序联系起来.我决定将该功能应用到我们的profile provider里(对XmlProfileProvider来说,Membership 和 Roles providers都可以使用它).Profile数据默认时存储在App_Data文件夹里的一个文件里,以阻止用户从浏览器直接请求这些文件(如果一个用户试图从浏览器访问http://www./App_Data/anyFile 的话,ASP.NET会返回一个HTTP 404消息)。不过你也可以通过 provider的profileFolder设置来指定XML文件存放在什么地方. 具体来说,Profile数据以如下形式进行存储:~/App_Data/XmlProfiles/applicationName/username.xml.每一个XML文件开始为一个<profileData>根元素,而子元素就由定义在Web.config文件里的Profile属性里决定了。 就像在Part 6探讨了一样,Profile属性可以为strings, XML,或binary数据,对string类型的属性而言,XML文件将其保存为文本形式;如果是XML类型则原封不动的保存;如果是binary类型的就保存为64位编码的binary.假设我们在Web.config文件<properties>元素里定义了6个Profile属性: <profile defaultProvider="..."> <providers> ... </providers> <properties> <add name="Birthdate" type="DateTime" serializeAs="String" /> <add name="HomepageUrl" type="String"/> <add name="Signature" /> <add name="IncludeSignatureInPosts" type="Boolean"/> <add name="BillingAddress" type="Address" serializeAs="Xml" /> <add name="ShippingAddress" type="Address" serializeAs="Binary" /> </properties> </profile> 其中的Address类型是定义在App_Code文件夹里的一个简单类,包含了些标量属性,比如:Address1, Address2, City等.注意:Profile属性BillingAddress序列化为XML,而ShippingAddress为binary. 对于上面定义的Profile属性,如果有个叫"Jisun"的用户登录我们的网站,使用Profile system,且应用程序的名称为"MyApp",那么将会创建一个~/App_Data/XmlProfiles/MyApp/Jisun.xml的文件,其包含的代码可能为: <?xml version="1.0" encoding="utf-8"?> <profileData> <IncludeSignatureInPosts>False</IncludeSignatureInPosts> <Signature>What is life, but a seemingly random collection of 1s and 0s?</Signature> <HomepageUrl>http://www.</HomepageUrl> <Birthdate>1979-04-01</Birthdate> <ShippingAddress><![CDATA[AAEAAAD/////AQAAAAAAAAAMAHBfQ29kZS5j...]]></ShippingAddress> <BillingAddress> <Address xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns:xsd="http://www./2001/XMLSchema"> <Address1>123 Anywhere St.</Address1> <Address2 /> <City>San Diego</City> <State>CA</State> <ZipCode>92109</ZipCode> </Address> </BillingAddress> </profileData> 就像你看见的那样,每个Profile属性被处理成一个XML元素并作为<profileData>根元素的子元素. 对string类型的属性,比如IncludeSignatureInPosts, Signature, HomepageUrl,以及Birthdate,其值在XML元素里为文本值;对XML类型的属性(BillingAddress),其值在<BillingAddress>元素里保存为原始形式;binary类型的属性ShippingAddress被编码为64位. 规划XmlProfileProvider Class类 当构建用户定制Profile provider时,我们要扩充System.Web.Profile.ProfileProvider class类.至少要包含2个方法——GetPropertyValues(context, collection) 和 SetPropertyValues(context, collection).这2个方法都接受2个输入参数——context 和 collection. 其中context提供的信息为:用户名以及该用户是否经过认证(ASP.NET支持匿名profile,不过这超出了本文的范围,更多信息请参阅文章《Profiles section on the ASP.NET Quickstarts 》(http:///QuickStartv20/aspnet/doc/profile/default.aspx#anon));另外collection提供一系列的Profile属性. 上面的GetPropertyValues方法返回所需Profile属性的值,相反SetPropertyValues方法的目的是向backing store(就本例而言,为XML文件)写入属性值.在ProfileProvider class类里还有其它的类,不过我们可以创建仅仅执行这2个方法的自定义profile provider. 在你的Website里使用XmlProfileProvider自定义Provider 为了在你的应用程序里使用自定义的XmlProfileProvider,我们首先要在本文结尾处下载其代码,将CustomProviders.dll放在你的web 应用程序的/bin目录,再更新你的Web.config文件: <profile defaultProvider="MyCustomProfileProvider"> <providers> <clear /> <add name="MyCustomProfileProvider" type="CustomProviders.XmlProfileProvider, CustomProviders" applicationName="applicationName" [profileFolder="baseFolderPathForProfileXMLFiles"] /> </providers> <properties> ... </properties> </profile> 其中profileFolder属性是可选的,如果忽略的话,默认为~/App_Data/XmlProfiles/. 下载代码里包含了一个测试的web应用程序,指导如何将自定义的XmlProfileProvider class类与默认的SqlMembershipProvider class类协调使用. 结语: 在本文我们看到如何利用provider model来创建以用户自定义的Profile provider,它将用户属性设置保存为XML文件而不是一个数据库. 祝编程快乐! 考察ASP.NET 2.0的Membership, Roles,Profile - Part 8 导言: 我最近遇到一个网站,其主要包含一些静态的内容.在客户端,需要一个页面来从一个数据库表里显示数据.另外,还需要一个页面来便于管理员添加、更新、删除数据.用ASP.NET 2.0的数据源控件以及Membership system,该功能可以很容易的实现。不过有一个问题,由于服务器不支持SQL Server数据库,只有用Access数据库来代替。问题来了,.NET Framework BCL只包含了一个支持Microsoft SQL Server数据库的Membership provider. 可喜的是,微软提供了一个Access数据库模板及在Membership, Roles,Profile里使用Access数据库的provider.在本文我们将探讨如何得到这些基于Access的provider以及如何在ASP.NET 2.0 web应用里使用它们.获取并使用Access Provider 在ASP.NET 2.0的Beta版本里,微软为Membership, Roles, Profile设计的基于Access和SQL Server数据库的provider,并作为基本类库的一部分包含在.NET Framework里.到ASP.NET 2.0 正式发行的时候,微软决定剔除内置的对Access数据库的支持,而只支持Microsoft SQL Server 2005 Express Edition版本数据库.但是不见得所有人都转移到SQL Server 2005.很多web服务器公司就不支持Express Edition.其它人可能在此之前设计的是基于Access数据库的应用程序并使用的很好,要转变到SQL Server的话可能困难重重. 还好,我们可以从微软单独下载到Access providers,其为一个Microsoft Visual Studio安装文件(VSI),包含了C# class类文件以及一个Access数据库.你可以在这里:http://download.microsoft.com/download/5/5/b/55bc291f-4316-4fd7-9269-dbf9edbaada8/SampleAccessProviders.vsi进行下载,当然在本文的下载文件里也包含它. 我们必须采取以下3个步骤才能使用Access providers: 1.将Access Provider Classes类应用到Web Application——下载内容里的C# class类文件要么被编译为一个文件并放到web应用程序的/bin目录文件夹里,要么将这些类文件拷贝到App_Code文件夹. 2.准备好数据库——确保应用程序里有一个Access数据库. 3.更新Web.config文件—必须对Web.config文件进行设置以使应用程序为Membership, Roles,Profile调用Access providers而不是默认的SQL Server providers. 第一步:将Access Provider Classes类应用到Web Application 下载完该VSI文件后,双击以进行安装过程,这将把文件安装到恰当的文件夹(就我的电脑而言:My Documents\Visual Studio 2005\Templates\ProjectTemplates\Visual Web Developer\Starter Kits).在“New Projects”对话框里可以找到一个模板,如下面的截屏所示: ![]() 创建一个新工程,用该ASP.NET Access Providers模板创建一个Class Library对象.在此,最理想的是将Class Library对象进行编译得到一个文件,比如AccessProviders.dll.再将其拷贝到应用程序的/bin目录下,这将允许你为Membership, Roles, Profile调用Access provider. 这是最理想的方法因此它便于部署和维护. 另外,你还可以将模板使用的C# class类文件拷贝到应用程序的App_Code文件夹里.最简单的方法是从下载的VSI文件里将其抽出来,在安装向导的第一步里,选择"View files in Windows Explorer",该VSI文件包含一个ZIP压缩文件(ASP.NET Access Providers.zip),在它的Samples目录下包含了必需的类.将这些类拷贝进App_Code文件夹. ![]() 第二步:准备好数据库 该VSI文件包含了一个Microsoft Access数据库(ASPNetDB.mdb),其包含了必需的表和视图.我们要将该数据库放在我们的应用程序里。我们可以在Windows资源管理器里查找到VSI里包含的Access数据库;或者安装完VSI后,我们也可以在硬盘里找到该数据库.不管是哪种方式,你都要将这个ASPNetDB.mdb数据库拷贝到App_Data文件夹.确保该文件没有标明为只读,这样那些用户帐户才有对App_Data文件夹的读写权限. 第三步:更新Web.config文件 最后我们要更新Web.config文件,以使Membership, Roles,Profile使用基于Access的provider而不是默认的基于SQL Server的provider.我们也需要在<connectionStrings>节点引用该Access数据库的路径. 首先添加新的<connectionStrings>,假定你的数据库还是ASPNetDB.mdb且放在App_Data文件夹里,你可以使用如下的代码: ![]() <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings> <add name="LocalAccessDatabase" connectionString="~/App_Data/ASPNetDB.mdb" providerName="System.Data.OleDb"/> </connectionStrings> <system.web> ... </system.web> </configuration> 接下来在<system.web>元素,添加必要的Membership, Roles,Profile元素.下面的声明清除了默认的Membership provider设置,而将AccessMembershipProvider作为应用程序默认的Membership provider: ![]() <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings> <add name="LocalAccessDatabase" connectionString="~/App_Data/ASPNetDB.mdb" providerName="System.Data.OleDb"/> </connectionStrings> <system.web> <membership defaultProvider="AccessMembershipProvider"> <providers> <clear/> <add name="AccessMembershipProvider" type="Samples.AccessProviders.AccessMembershipProvider, AssemblyName" connectionStringName="LocalAccessDatabase" enablePasswordRetrieval="false" enablePasswordReset="false" requiresUniqueEmail="false" requiresQuestionAndAnswer="false" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" applicationName="SampleSite" hashAlgorithmType="SHA1" passwordFormat="Hashed"/> </providers> </membership> </system.web> </configuration>
|
|
来自: laixuexideren > 《profile》