原文地址:http://www.odetocode.com/Articles/427.aspx
翻译:欧盟特软件技术公司 纪俊
很多年来,我们写了很多代码来实现互联网应用程序的窗体浏览权限管理。我们编码来验证用户名和密码、编码对密码做哈希并校验并且编码来创建与管理用户。如果你比较两个这种实现,你可能会发现程序结构与代码是相似的。从 ASP.net 2.0 开始,网站开发者不再需要写这种重复性的代码来存储与验证信息了。取而代之的是 ASP.net 2.0 提供了 Membership Provider 与 Role Provider 作为安全与科延伸的实现来管理应用程序的角色与权限。
在这当中,Membership Provider 与 Role Provider 给我们的应用程序提供验证与授权服务。验证是识别用户身份的过程。Membership Provider 可以在数据库中创建新的用户名与码,并且通过使用储存的信息验证用户的身份。针对活动目录也有一个 Membership Provider,不过这篇文章将会集中精力在 SQL Server Membership Provider。
ASP.net 2.0 提供了可以拖放到网页而不用编码就实现验证的登陆控件。这些控件直接跟 Membership Provider 对话。ASP.net 2.0 也提供了控件来维护动态的用户信息,包括修改与重置密码。所有这些控件都建立在 Membership Provider 得特性之上。
一旦我们知道了用户是谁。,我们就能明白我们允许用户做什么-这就是授权。ASP.net 2.0 得 Role Provider 允许我们创建角色,并且映射多个用户到对应的角色中。举例而言,你可能为应用程序设置两种角色:管理员与注册用户。给出一个用户名,Role Provider可以告诉我们这个用户属于哪个角色。网站应用程序的区域化或者特殊操作可以被限制在精确的角色里。
当然,您的应用程序可能有特殊的需要。可能你的数据库不是 Microsoft SQL Server。幸运的是,微软用一种可扩展的支持模型来实现 Membership Provider 与 Role Provider。这种支持模型是 Membership Provider 与 Role Provider 服务的关键点,所以我们先从解释 Provider 如果运作来展开我们的文章。
ASP.net 2.0 的 Provider 模型为开发者提供了将他们自己的实现作为一种特性加入到运行时的可扩展方法。Membership Provider 与 Role Provider 在 ASP.net 2.0 中都通过细化一个接口或者协议来遵循 Provider 的模型。如果你创建你的组件来实现 Provider 模型定义的协议,你可以插入你的代码到 ASP.net 运行时并且替换或者扩展已经存在的 Provider。在 ASP.net 2.0 的 Provider 模型包括一个 Provider 配置与初始化的基础结构。
Provider 模型从抽象类 ProviderBase 开始。ProviderBase 强迫协议为所有的 Provider 需要公共名称与描述属性,就像一个公共初始化方法一样。MembershipProvider 与 RoleProvider 是继承自 ProviderBase 的抽象类。这些类添加额外的属性与方法来定义它们特殊功能的接口。
举例来说,MembershipProvider 需要一个 membership 类来实现一个 ValidateUser 方法。2.0中默认的 Membership Provider ,SqlMembershipProvider,通过在一个 SQL Server 数据库中执行一个存储过程实现了这个方法。如果你想自己写 Provider 是来使用 XML 文件存储成员资格信息,你将不得不自己写 ValidateUser 的代码来通过存储在 XML 文件中的信息来验证用户的密码。
Provider 模型的美妙之处在于:高层的应用程序服务可以在 Provider 上建立并且不需要知道接口之后的细节。一个很好的例子是 ASP.net 2.0 的 Membership Provider 控件,包括 Login 控件,CreateUser 控件,LoginStatus 控件等等。所有这些控件遵循 MembershipProvider 协议。在某些时候,Login 控件需要在配置好的 Provider 中调用 ValidateUser 方法。Login 控件不关心是否有对 SQL Server 数据库或者 XML 文件的访问。所有的 Login 控件关心的是通过用户名与密码在返回值中得到的是真还是假。
MembershipProvider 的作用在于在 Membership Provider 控件之间提供一个间接层,就像 LoginControl,与成员资格信息的数据存储。间接意在我们可以使用任意的数据存储(SQL Server,Oracle,XML,Web Service,Active Directory),只要我们有一个 Provider 在实体类的公共接口与属性后隐藏细节。就像我们之前提到的,ASP.net 2.0 包括了 SQL Server 与 Active Directory 的 Provider 。
.NET 成功安装之后会在 System.Web 里设置 SqlMembershipProvider 类为默认的 Membership Provider。你可以在 machine.config 中找到默认的配置,这个文件的配置对当前计算机管理的所有应用程序都有效。machine.config 文件可以在 .net framework 安装的配置路径找到,典型的是\Windows\Microsoft.NET\Framework\v2.0.xxxx.
<membership>
<providers>
<add
name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, ..."
connectionStringName="LocalSqlServer"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
applicationName="/"
requiresUniqueEmail="false"
passwordFormat="Hashed"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="7"
minRequiredNonalphanumericCharacters="1"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""
/>
</providers>
</membership>
machine.config 中有很多跟 Membership Provider 有关的配置选项我们可以使用-它们中的需要包括用户密码管理。
passwordFormat 属性详细说明了 Provider 怎样存储密码,并且会影响许多其他 Membership Provider 的作用。SqlMembershipProvider 支持三种格式:Hashed(默认的也是最安全的格式)、Encrypted、Clear。在存储密码前用哈希函数对无格式的用户密码与随机值通过不可逆哈希算法加密,这种方法不能获得哈希之前的原密码。为了验证密码, Provider 不得不比较哈希之后的输入密码与存储的密码。 Provider 也可以存储加密的密码(这种方法可以解密并且获得原先的值),或者存储不经处理的密码(这种方法不推荐使用)。
enablePasswordRetrieval 选项决定 Provider 是否可以通过 GetPassword 方法返回用户的密码。如果密码格式设置为 Hashed。密码不能重新获得。如果 Provider 存储的是加密的或者是未经处理的文本格式,你可以电邮他们忘记的用户密码,但是需要考虑到安全性。在密码忘记时一个更安全的选项是重置用户的密码为一个新值并电邮给用户(通过 requiresUniqueEmail 确认用户输入正确的电子邮件地址)。
enablePasswordReset 选项控制 ResetPassword API。ResetPassword 可以设置一个新的、自动分配的密码给用户。PasswordRecovery 控件可以自动的电邮新密码给用户。把 requiresQuestionAndAnswer 选项设置为真来防止恶意用户重置其他人密码是个好方法。设置为真意味着用户必须在重置密码之前提供安全问题的答案。问题与答案文本在 CreateUser 控件添加新用户的时候需要提供。
Provider 允许很多属性来控制密码的强度。minRequiredPasswordLength 与 minRequiredNonalphanumericCharacters 防止用户使用“abc”这样简单的密码。如果有特殊的需求,你可以使用 passwordStrengthRegularExpression 属性来强制密码通过一个常规表达式测试。注意:ResetPassword 产生的密码总是满足密码长度与非数字字符串数量要求,但是可能不能通过常规表达式测试。
SqlMembershipProvider 提供了许多以上还没提到的特性。例如:maxInvalidPasswordAttempts 与 passwordAttemptWindow 属性合作可以用来防止恶意用户暴力破解用户的账号。输入错误台多次会使账号被锁定直到使用 UnlockUser 方法来解锁。
在 Membership Provider段的其他属性控制 SqlMemeberShipProvider 怎样与 SQL Server 交互。默认的,machine.config 文件配置 Membership Provider 与 Role Provider 以及使 App_data 路径的 SQL Server Express 数据库文件配合。上面提到的配置,我们看到 connectionStringName 属性是“LocalSqlServer”。如果你找到了 machine.config 连接字符串段,你会发现如下信息:
<add name="LocalSqlServer"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
您可以覆盖默认设置并设置所有的 Provider 使用 LocalSqlServer 或远程数据库或者本地的非结构化数据库。第一步是使用 ASP.net Sql Server Registration Tool(aspnet_regsql.exe)来创建新数据库。你可以在 .net framework 安装路径(WINDOWS\Microsoft.NET\Framework\2.0.xxxx)找到这个工具。如果你不使用命令行参数直接启动工具,程序会启动一个向导来创建新数据库。默认数据库名称为 aspnetdb。
当你配置好了 Provider 使用的数据库,你可以通过修改 web.config 文件或者你的应用程序来重新定义 LocalSqlServer 连接字符串来指向新数据库。
<connectionStrings>
<remove name="LocalSqlServer"/>
<add name="LocalSqlServer"
connectionString="server=.;database=aspnetdb;integrated security=sspi;"/>
</connectionStrings>
或者,你可以定义一个新连接字符串并修改 connectionStringName 属性来使 Provider 使用新连接字符串。
可以使用 ASP.net 配置工具(站点菜单下是 Visual Studio)来测试您的设置。在 Provider 栏选择“Select a different provider for each feature”,接下来的页面会允许您测试 Provider 的连接属性。管理工具页面还包括安全性、创建用户等等功能。
Membership Provider配置的另一个重要属性是 applicationName。applicationName 允许一个数据库支持多个网站应用程序。如果有两个网站应用程序并且想让他们共享同一个用户数据库,给他们同一个 applicationName 属性并且指向同一个 aspnetdb 数据库。如果想让他们使用同一个数据库但是不同用户,给他们每一个唯一的 applicationName 属性。
如果想与 Membership API 直接交互,方法之一是使用 System.Web.Security 中的 Membership 类。Membership 类仅仅包括静态成员与属性,但是这些静态成员在 MembershipProvider 中映射到属性与方法,并且组件会在适当的情况下调用配置好的 Provider 。下面是一个使用编码来实现用户属性的例子。
string username = "SwedishChef";
string password = "bj#kbj1k";
string email = @"[email protected]";
string question = "The greatest band ever?";
string answer = "ABBA";
bool isApproved = true;
MembershipCreateStatus status;
Membership.CreateUser(
username, password, email,
question, answer, isApproved,
out status);
if(status == MembershipCreateStatus.Success)
{
// party!
}
一个早期的Membership Provider 接口使用 ASP.net 2.0 Login 控件:Login 、LoginView、PasswordRecovery、LoginStatus、LoginName、CreateUserWizard与ChangePassword。Login 控件,举例而言,当用户输入他们的用户名与密码并且点击登陆按钮时会使用当前Membership Provider 的 ValidateUser 方法。如果内建控件提供了所有您所需的功能,不需要编写任何代码。所有的控件可以被设置为各种风格通过样式与模板。在 Visual Studio 工具条的“Login”分类下可以找到这些控件。