ASP.NET 成员资格 Part.1(API)
表单验证解决了 ASP.NET 应用程序页面验证的问题,但另一方面,必须自己实现登录表单与底层的凭证存储之间的通信。对每一个 WEB 程序来说,这些工作几乎都是相同的,非常乏味。
而且,表单验证只提供了验证用户的架构。如果你使用一个自定义的凭证存储,就必须自己写管理用户的管理应用程序,它应当实现添加用户、删除用户、重设密码等功能。这又是一串对于 Web 程序来说差不多的工作。
ASP.NET 2.0 引入了成员资格 API,ASP.NET 4.0 保留了它。成员资格 API 是以现有的表单验证架构为基础的框架。
ASP.NET 成员资格 API 简介
成员资格 API 框架提供了一整套完整的用户管理功能:
- 通过编程或者 ASP.NET 的 Web 配置工具来创建和删除用户
- 重置用户密码,还可以自动发送密码重置邮件
- 如果用户通过后台程序创建,可以为用户自动生成密码,还可以将密码通过邮件自动发送给用户
- 提供了一系列预建控件创建登录页面、注册页面、显示登录状态、为验证过和未验证过的用户提供不同的视图
- 通过成员资格提供程序类为应用程序提供了一个抽象层,以上所有功能都可以不依赖底层的数据存储而完全独立工作。底层的数据存储也可以在不需要修改程序的情况下,使用其他类型的数据存储机制来替换。默认情况下,成员资格 API 使用 SQL Server Express 数据库存储用户和角色信息
成员资格 API 的结构(顺序亦为层次关系):
- 安全控件:登录控件、密码控件、创建用户向导、状态控件
- 成员资格 API :Membership 类、[MembershipUser 类]
- 成员资格提供程序:SQL Server 提供程序、活动目录提供程序、自定义的提供程序
- 成员资格存储:SQL Server、活动目录、自定义的存储 (例如,Oracle)
成员资格 API 的设计使其可以完全独立于它的底层数据存储来工作。作为程序的开发人员,主要就是和 ASP.NET 提供的控件以及 Membership 类打交道。所有与成员资格 API 相关的类都被放在了 System.Web.Security 命名空间中。
成员资格 API 相关的类:
Membership | 这个类是和成员资格 API 交互的主要部分,它提供了一组方法来管理用户、验证用户以及重置用户密码 |
MembershipCreateUserException | 当视图通过 Membership 类创建用户时,如果发生错误,会抛出一个异常 |
MembershipUser | 代表一个存储在成员资格 API 凭证存储中的单独用户。包含此用户的所有信息,Membership 类有好几个方法返回此对象,例如 GetUser() |
MembershipUserCollection | 成员资格用户的集合,例如 Membership.GetAllUsers() 可以返回此对象 |
MembershipProvider | 继承这个基类可以创建自定义的成员资格提供程序,这样就可以根据你自定义的凭证存储来验证用户了 |
MembershipProviderCollection | 计算机以及 Web 应用程序的所有可用成员资格提供程序的集合 |
SqlMembershipProvider | MembershipProvider 类的一个实现,与 SQL Server 数据库一同工作 |
ActiveDirectoryMembershipProvider | MembershipProvider 类的一个实现,与活动目录一同工作 |
ActiveDirectoryMembershipUser | 这个类继承了 MembershipUser 的所有功能,并添加了一些与活动目录相关的属性 |
成员资格只用来管理和验证用户,它不实现任何授权功能,也不提供管理用户角色的功能。因此,你必须使用角色 API(后续文章会介绍)。
使用成员资格 API
使用成员资格 API 和 安全控件之前,必须完成以下步骤:
- 在 web.config 文件中配置表单验证、禁止匿名用户访问
- 建立成员资格数据存储。你必须在一个你选择的数据库中创建一些表和存储过程
- 配置数据库连接字符串和你想要在应用程序的 web.config 文件中使用的成员资格提供程序
- 创建登录页面,使用 Membership 类验证用户输入的凭证
使用 ASP.NET WAT 可以完成除了提供程序配置以外的任何配置步骤。ASP.NET WAT 包含了一个安全向导,在 VS 中选择“网站”-“ASP.NET 配置”即可:
如果在一台运行 SQL Server Express 的机器上使用 WAT,当创建第一个用户时,底层的数据存储会自动创建。当然,如果你通过编程访问成员资格数据存储,它也会自动创建,这个功能是 SqlMembershipProvider 提供的。如果是其他版本的 SQL Server,那么就需要手动配置数据源。
当使用 SQL Server Express 版本时,SqlMembershipProvider 会在 App_Data 目录中创建一个名为 ASPNETDB.MDF 的新数据库。这个数据库实现完整的架构,它是保存用户信息、角色信息、用户角色赋权、个性化和用户配置属性所必需的。
如果你想使用自己的数据库而不是自动创建的数据库来存储用户信息和角色信息,你必须在运行安全向导之前配置成员资格提供程序和这个提供程序的连接信息。
1. 配置表单验证
成员资格 API 建立在表单验证的基础上,它提供了一个现成的架构管理和验证用户。因此,必须配置程序来使用表单验证。
通常,根目录对匿名用户来说是可以访问的,它一般包含登录页面和注册页面之类的功能。因此,在根的 web.config 中只需包含启用表单验证即可:
<system.web>
<authentication mode="Forms" />
</system.web>
在子目录中,你可以再添加一个 web.config 配置文件来禁止匿名用户访问网站的受保护的资源,如果有人没有经过验证就视图访问这个目录下的资源,ASP.NET 运行时会自动将用户重定向到登录页面。
<authorization>
<deny users="?"/>
</authorization>
2. 创建数据存储
之前提过,当和 ASP.NET 一起使用 SQL Server Express 版本时,SqlMembershipProvider 会自动创建这个存储。但如果是其他版本的 SQL Server,你必须手工创建这个存储。
不使用基于文件自动附加的数据库的方式还有一些其他原因:性能和并发性。
SQL Server Express 可以以两种方式使用数据库:
- 传统模式:创建或附加一个数据库到 SQL Server 服务,然后 SQL Server 会对数据库有完全的控制并能够把该数据库并发地提供给多个应用程序和用户。
- 文件模式:你的程序能直接访问 SQL Server 数据库文件,不需把该文件附加到 SQL Server 实例。在需要时,SQL Server Express 实例会动态的附加或分离此文件,文件只被锁定一段较短的时间,这不但使得对数据库文件的复制变得很容易,同时,在访问时需要额外的负载。另外,在为某个应用程序附加数据库期间,其他应用程序都不能访问该数据库,因为它被当前活动的应用程序锁定了。这种模式适合 Windows 客户端应用程序和开发,但不适合生产环境。
对于生产环境,建议手工创建成员资格数据库。对于 SqlMembershipProvider ,创建这样一个数据存储意味着创建一个 SQL Server 数据库并在这个库里创建一组表和存储过程。ASP.NET 发布了一系列 SQL 脚本可以用来手工创建数据库和表。ASP.NET 还发布了一个工具,它可以在你选择的数据库里创建这些数据库表和存储过程。
这个工具叫作 aspnet_regsql.exe。通过一个向导页面或者命令行使用专门的命令行开关都可以很方便的运行它。无论哪种情况,都必须从“Visual Studio 命令行提示”窗口中启动它,因为它包含了含有必需工具的 .NET Framework 目录的路径信息。如果不加任何参数启动这个工具,该工具会启动一个向导界面,它引导你通过一系列步骤创建数据库:
也可以通过命令行使用 aspnet_regsql.exe。实际上,这是自动化程序安装的好方法。比如,要创建成员资格 API 数据库表,可以执行下面的命令:
aspnet_regsql.exe –S (local)\SQLEXPRESS –E –A all –d MyDatabase
aspnet_regsql.exe 的命令行开关
-S 服务器名 | 指定 SQL Server 和你想要为其安装 ASP.NET 数据库表的实例 |
-U 用户名 | 如果不用 Windows 验证连接 SQL Server,那这是必需的 |
-P 密码 | 如果指定了 –U 开关,也需要指定密码开关 |
-E | 显式的指定使用 Windows 验证连接到 SQL Server |
-C | 允许指定一个用来连接数据库的完整的 ODBC 或者 OLEDB 连接字符串 |
-sqlexportonly | 未添加和删除指定的选项生成 SQL 脚本,而无须在专门的 SQL Server 实例上安装它们 |
-A | 安装应用程序服务。这个参数有效选项为 all、m[成员资格]、r[角色服务]、p[支持用户配置的 ASP.NET 用户配置]、c[Web 部分页面的个性化]、w[SQL Web 事件提供程序]。 |
-R | 卸载应用程序服务。有效选项和 –A 相同 |
-d | 让你随意指定要按照应用程序服务的数据库的名称。默认为 aspnetdb |
aspnet_regsql.exe 工具还包含了另外几个开关,用于安装基于 SQL Server 的会话状态以及配置 SQL 缓存依赖(参考之前系列文章)。
1. ASP.NET 服务的数据库脚本
aspnet_regsql.exe 执行几个脚本来创建或删除成员资格相关的数据库表。这些脚本由 .NET Framework 提供:
基本上存在 2 种类型的脚本:InstallXXX 和 UninstallXXX,你们懂的。
成员资格 API 的安装脚本
InstallCommon.sql | 安装成员资格 API 和角色 API 所必需的一些公用的表和存储过程 |
InstallMembership.sql | 安装成员资格 API 使用的表、存储过程和触发器。包含用户表、额外用户属性表和用来访问这些信息的存储过程表 |
InstallRoles.sql | 安装关联用户和程序角色所需要的所有数据库表和存储过程,用于授权 |
InstallPersonalization.sql | 包含用来创建表和存储过程的 DDL。这些表和存储过程是创建带有 Web 部件的个性化网上门户应用程序所必需的 |
InstallProfile.sql | 创建用来支持 ASP.NET 用户配置的所有必需的表和存储过程 |
InstallSqlState.sql | 在 SQL Server 的 TEMP 数据库中安装持久会话状态所需的表。这意味着每次 SQL Server 服务关闭时,会话状态就会丢失 |
InstallPersistSqlState.sql | 在一个单独的 ASPState 数据库中安装持久会话状态所需的表。这意味着即使 SQL Server 服务重新启动,状态仍然存在 |
如果你不想或不能使用 aspnet_regsql.exe,也可以使用 sqlcmd.exe 命令行工具来执行这些脚本:
sqlcmd –S (local)\SQLEXPRESS –E –i InstallCommon.sql
2. 基于文件的 SQL Server 存储
之前已经说过,这种模式允许你通过 MDF 文件直接访问 SQL Server 数据库,而不需要在一个 SQL Server 实例中创建或者附加它们。只需将数据库文件复制到目标服务器上即可。不过,基于文件的模式只用于 Express 版本,大版本不支持这种模式,因为它通常对于高度可扩展的生产环境不实用。
3. 配置连接字符串和成员资格提供程序
如果使用自己的 SQL Server 数据库,甚至自定义的成员资格提供程序和数据存储,则必须正确的配置提供程序和恰当的连接到成员资格存储数据库的连接字符串。
比如,你想使用一个名为 MyDatabase 的本地数据库配置连接字符串:
<connectionStrings>
<add name="MyMembershipConnString"
connectionString="data source=(local)\SQLEXPRESS;
Integrated Security=SSPI;
Initial Catalog=MyDatabase"/>
</connectionStrings>
还必须为应用配置成员资格提供程序,必需把 <membership> 节添加到 web.config 文件的 <system.web> 中:
<membership defaultProvider="MyMembershipProvider">
<providers>
<add name="MyMembershipProvider"
connectionStringName="MyMembershipConnString"
applicationName="MyMembership"
enablePasswordRetrieval="false"
enablePasswordRest="true"
requiresQuestionAndAnswer="true"
requiresUniqueEmail="true"
passwordFormat="Hashed"
type="System.Web.Security.SqlMembershipProvider"/>
</providers>
</membership>
在 <membership> 节中可以添加多个作为 <providers> 节的子元素。defaultProvider 特性非常重要,这个特性指定你的应用程序将使用的成员资格提供程序。
SqlMembershipProvider 的属性
name | 可任意指定提供程序名称。编程访问已配置的成员资格提供程序列表时,这个名字可作引用。此外,WAT 使用它显示提供程序 |
applicationName | 指定成员资格提供程序为之管理好用户和用户设置的程序名。可以为多个应用程序使用一个成员资格数据库。用户和角色总是和应用程序相关联。 |
description | 描述。可选 |
passwordFormat | 获取或设置密码在底层凭证存储中的存储格式。Clear(纯文本)、Encrypted(用本地配置的机器码加密密码)、Hashed(散列化密码) |
minRequiredNonalphanumericCharacters | 指定密码必需含有的非字母数字字符的个数。重要,这是指定用户所使用密码的强度要求 |
minRequiredPasswordLength | 密码最短长度。这也是一个指定密码强度的指标 |
passwordStrengthRegularExpression | 可以使用正则表达式指定一个有效的密码格式,这就可以非常灵活的指定密码的标准格式了 |
enablePasswordReset | 可以重置密码。如果程序中配置了一个 SMTP 服务器,它还会发送一封 Email |
enablePasswordRetrieval | 如果为 true,通过 MembershipUser.GetPassword() 可以取回密码。当然,这只有在密码没有被散列化的情况下才起作用 |
maxInvalidPasswordAttempts | 指定用户被锁定之前无效验证重试的次数。默认值 5 。 |
passwordAttemptWindow | 设置以分钟计的时间。在这段时间内可以尝试多次无效密码或者密码回答,时间一过,用户立即被锁定。默认值 10 分钟。管理员必须再次激活这个账号 |
requiredQuestionAndAnswer | 指定程序是否启用密码问题功能。用户忘记密码后并正确回答了问题,他可以通过 Email 重新获得一个自动生成的密码 |
requiredUniqueEmail | 在底层的成员资格存储中指定用户的 Email 地址是否必须唯一 |
applicationName 属性要特别注意。实际上,成员资格数据库里的所有对象都和一个应用程序相关联。如果没有在成员资格配置里指定 applicationName,API 或 WAT 会把这些对象通过名称“/”关联到根应用程序。
4. 创建并验证用户
启动 WAT 切换到安全标签,选择创建用户:
创建几个用户后,查看数据库的 aspnet_Users 表和 aspnet_Membership 表:
密码和密码问题的答案都以散列字符串的形式存放在数据库中,因为你在 <membership> 配置节中对提供程序选择了 passwordFormat=”Hashed” 。
现在可以使用成员资格 API 来验证这些用户了。随便创建一个登录页面,使用下面代码来验证用户凭证:
protected void btnLogin_Click(object sender, EventArgs e)
{
// Membership: 验证用户凭据并管理用户设置
// ValidateUser(): 验证提供的用户名和密码是有效的
if (Membership.ValidateUser(txtName.Text, txtPwd.Text))
{
FormsAuthentication.RedirectFromLoginPage(txtName.Text, false);
}
else
{
LegendStatus.Text = "Invalid user name or password.";
}
}
你并不需要知道应用程序具体使用的是哪个应用程序(Membership 是它们的基类)。如果要换提供程序只需修改配置文件。