本来想在第三篇文章里介绍一下 Membership的类的,不过现在中文msdn也出来了,所以就不写了,,直接到介绍Membership Providers。
       Membership Providers提供了Membership数据源和服务之间的所有接口,在Asp.net2.0中提供了两个Provider:SqlMembershipProvider和ActiveDirectoryMembershipProvider,从命名中我们也可以看出,SqlMembershipProvider是把 sql server和sql server express数据库作为数据库源,而ActiveDirectoryMembershipProvider是有Microsoft Active Directory(活动目录)作为数据源的。
        Membership Providers的基本工作是用来管理一个网站注册用的数据,并且提供一些函数用来做 创建,删除用户,验证用户登录信息,修改密码等工作。在.net 框架的System.Web.Security命名空间下有一个MembershipUser的类,这个类定义了Membership 用户的一些基本属性,Membership Provider用这个类来描绘每个用户信息。Membership Providers 有一个基础类 他的定义如下:(丛msdn里copy出来的)
 public abstract class MembershipProvider : ProviderBase
public abstract class MembershipProvider : ProviderBase

 {
{ // Abstract properties
    // Abstract properties
 public abstract bool EnablePasswordRetrieval
    public abstract bool EnablePasswordRetrieval  { get; }
{ get; }
 public abstract bool EnablePasswordReset
    public abstract bool EnablePasswordReset  { get; }
{ get; }
 public abstract bool RequiresQuestionAndAnswer
    public abstract bool RequiresQuestionAndAnswer  { get; }
{ get; }
 public abstract string ApplicationName
    public abstract string ApplicationName  { get; set; }
{ get; set; }
 public abstract int MaxInvalidPasswordAttempts
    public abstract int MaxInvalidPasswordAttempts  { get; }
{ get; }
 public abstract int PasswordAttemptWindow
    public abstract int PasswordAttemptWindow  { get; }
{ get; }
 public abstract bool RequiresUniqueEmail
    public abstract bool RequiresUniqueEmail  { get; }
{ get; }
 public abstract MembershipPasswordFormat PasswordFormat
    public abstract MembershipPasswordFormat PasswordFormat  { get; }
{ get; }
 public abstract int MinRequiredPasswordLength
    public abstract int MinRequiredPasswordLength  { get; }
{ get; }
 public abstract int MinRequiredNonAlphanumericCharacters
    public abstract int MinRequiredNonAlphanumericCharacters  { get; }
{ get; }
 public abstract string PasswordStrengthRegularExpression
    public abstract string PasswordStrengthRegularExpression  { get; }
{ get; } // Abstract methods
    // Abstract methods public abstract MembershipUser CreateUser (string username,
    public abstract MembershipUser CreateUser (string username,  string password, string email, string passwordQuestion,
        string password, string email, string passwordQuestion,  string passwordAnswer, bool isApproved, object providerUserKey,
        string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status);
        out MembershipCreateStatus status); public abstract bool ChangePasswordQuestionAndAnswer
    public abstract bool ChangePasswordQuestionAndAnswer (string username, string password,
        (string username, string password, string newPasswordQuestion, string newPasswordAnswer);
        string newPasswordQuestion, string newPasswordAnswer); public abstract string GetPassword (string username,
    public abstract string GetPassword (string username, string answer);
      string answer); public abstract bool ChangePassword (string username,
    public abstract bool ChangePassword (string username, string oldPassword, string newPassword);
        string oldPassword, string newPassword); public abstract string ResetPassword (string username,
    public abstract string ResetPassword (string username, string answer);
        string answer); 
  public abstract void UpdateUser (MembershipUser user);
    public abstract void UpdateUser (MembershipUser user); public abstract bool ValidateUser (string username,
    public abstract bool ValidateUser (string username, string password);
        string password); public abstract bool UnlockUser (string userName);
    public abstract bool UnlockUser (string userName); public abstract MembershipUser GetUser (object providerUserKey,
    public abstract MembershipUser GetUser (object providerUserKey, bool userIsOnline);
        bool userIsOnline); public abstract MembershipUser GetUser (string username,
    public abstract MembershipUser GetUser (string username, bool userIsOnline);
        bool userIsOnline); public abstract string GetUserNameByEmail (string email);
    public abstract string GetUserNameByEmail (string email); public abstract bool DeleteUser (string username,
    public abstract bool DeleteUser (string username, bool deleteAllRelatedData);
        bool deleteAllRelatedData); public abstract MembershipUserCollection GetAllUsers
    public abstract MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords);
        (int pageIndex, int pageSize, out int totalRecords); public abstract int GetNumberOfUsersOnline ();
    public abstract int GetNumberOfUsersOnline (); public abstract MembershipUserCollection FindUsersByName
    public abstract MembershipUserCollection FindUsersByName (string usernameToMatch, int pageIndex, int pageSize,
        (string usernameToMatch, int pageIndex, int pageSize, out int totalRecords);
        out int totalRecords); public abstract MembershipUserCollection FindUsersByEmail
    public abstract MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize,
        (string emailToMatch, int pageIndex, int pageSize, out int totalRecords);
        out int totalRecords); // Virtual methods
    // Virtual methods protected virtual byte[] EncryptPassword (byte[] password);
    protected virtual byte[] EncryptPassword (byte[] password); protected virtual byte[] DecryptPassword (byte[] encodedPassword);
    protected virtual byte[] DecryptPassword (byte[] encodedPassword); protected virtual void OnValidatingPassword
    protected virtual void OnValidatingPassword (ValidatePasswordEventArgs e);
        (ValidatePasswordEventArgs e); // Events
    // Events public event MembershipValidatePasswordEventHandler
    public event MembershipValidatePasswordEventHandler ValidatingPassword;
        ValidatingPassword; }
}
SqlMembershipProvider类就是从这个类里继承下来的。
接下来 我们使用SqlMembershipProvider类作为例子来进行比较细致的说明。
SqlMembershipProvider:
SqlMembershipProvider是给Membership使用sql server数据库做的Provider,它使用数据库的存储过程来实现对数据的操作,这样SqlMembershipProvider可以经过很少的改动来 实现对其他数据库的支持。
1.Provider 初始化
Provider初始化是在 SqlMembershipProvider.Initialize,它只运行一次,是在asp.net装载Provider时。
a.初始化SqlMembershipProvider的各种属性 比如:EnablePasswordRetrieval 和 EnablePasswordReset,从相应的配置文件的配置属性中读入。
b.对一些公共属性的值进行检查,当有错误的时候抛出异常,比如PasswordFormat值是”hashed”,而EnablePasswordRetrieval的值是true,就会有异常抛出。
c.在配置里存在一些不被承认的属性时,也会抛出异常
SqlMembershipProvider.Initialize还会从<connectionStrings>中读取数据库连接字符串,保存到一个私有的变量中,如果不能读到或者读取的连接字符串是错误的,也会抛出一个异常。
2.数据定义
SqlMembershipProvider的Membership数据保存在数据库的aspnet_Membership表中
aspnet_Membership 定义(msdn中取出)
| 字段名 | 字段类型 | 表述 | 
| ApplicationId | uniqueidentifier | Application ID,应用程序id | 
| UserId | uniqueidentifier | User ID,用户id | 
| Password | nvarchar(128) | 密码,可以是加密 hash保存的 | 
| PasswordFormat | int | Password format (0=Plaintext, 1=Hashed, 2=Encrypted) | 
| PasswordSalt | nvarchar(128) | Randomly generated 128-bit value used to salt password hashes; stored in base-64-encoded form | 
| MobilePIN | nvarchar(16) | User's mobile PIN (当前没有使用) | 
|  | nvarchar(256) |  | 
| LoweredEmail | nvarchar(256) | 小写email地址 | 
| PasswordQuestion | nvarchar(256) | 密码问题 | 
| PasswordAnswer | nvarchar(128) | 密码问题答案 | 
| IsApproved | bit | 1=Approved, 0=Not approved | 
| IsLockedOut | bit | 1=Locked out, 0=Not locked out | 
| CreateDate | datetime | 创建时间 | 
| LastLoginDate | datetime | 最后登录时间 | 
| LastPasswordChangedDate | datetime | 密码最后修改时间 | 
| LastLockoutDate | datetime | 最后登出的时间 | 
| FailedPasswordAttemptCount | int | 联系登录失败次数 | 
| FailedPasswordAttempt-WindowStart | datetime | 在FailedPasswordAttemptCount非零时,第一次登录失败的时间 
 | 
| FailedPasswordAnswer-AttemptCount | int | 回答密码问题联系失败的次数 | 
| FailedPasswordAnswer-AttemptWindowStart | datetime | 在FailedPasswordAnswer-AttemptCount非零时,第一次回答问题失败的时间 | 
| Comment | ntext | 扩展的文本 | 
这个表中的每一条记录代表一个用户,这个表还有两个外键,分别关联aspnet_Applications表和aspnet_Users表
aspnet_Applications表
| 字段名 | 字段类型 | 描述 | 
| ApplicationId | uniqueidentifier | Application ID | 
| ApplicationName | nvarchar(256) | Application name | 
| LoweredApplicationName | nvarchar(256) | Application name (小写) | 
| Description | nvarchar(256) | Application 描述 | 
aspnet_Users 表
| 字段名 | 字段类型 | 描述 | 
| ApplicationId | uniqueidentifier | Application ID | 
| UserId | uniqueidentifier | 用户ID | 
| UserName | nvarchar(256) | 用户名 | 
| LoweredUserName | nvarchar(256) | 用户名 (小写) | 
| MobileAlias | nvarchar(16) | User's mobile alias (currently not used) 没有使用 | 
| IsAnonymous | bit | 1=Anonymous user, 0=Not an anonymous user | 
| LastActivityDate | datetime | 用户最后一次活动时间 | 
一条完整的记录 aspnet_Membership和aspnet_Users都要存在。
3.数据访问
SqlMembershipProvider是通过存储过程完成所有的数据库操作的,简单介绍一下这些存储过程
SqlMembershipProvider存储过程
| 名称 | 描述 | 
| aspnet_Membership_ChangePassword-QuestionAndAnswer | 修改密码,密码问题,密码问题答案 | 
| aspnet_Membership_CreateUser | 增加一个membership用户 记录同时写入aspnet_Users 和aspnet_Membership 表, 如果需要,还要增加一条记录到aspnet_Applications表。 | 
| aspnet_Membership_FindUsersByEmail | 通过email地址匹配查找用户的记录,同时还要一个application ID. | 
| aspnet_Membership_FindUsersByName | 通过用户名匹配查找用户的记录,同时还要一个application ID. | 
| aspnet_Membership_GetAllUsers | 得到所有用户记录,在一个下application ID. | 
| aspnet_Membership_GetNumberOfUsersOnline | 得到在线用户数 (通过用户最后活动时间字段(LastActivityDate)实现) | 
| aspnet_Membership_GetPassword | 得到一个用户的密码,通过密码问题的回答 | 
| aspnet_Membership_GetPasswordWithFormat | 得到一个用户的密码。通过密码比较重新取得密码。 | 
| aspnet_Membership_GetUserByEmail | 使用email和application id从aspnet_Membership中得到相应的记录 | 
| aspnet_Membership_GetUserByName | 使用用户名和application id从aspnet_Membership中得到相应的记录 | 
| aspnet_Membership_GetUserByUserId | 使用userid和application id从aspnet_Membership中得到相应的记录 | 
| aspnet_Membership_ResetPassword | 重置用户密码通过回答密码问题 | 
| aspnet_Membership_SetPassword | 设置一个密码 | 
| aspnet_Membership_UnlockUser | 恢复用户登录的权限通过将IsLockedOut字段设置成0 | 
| aspnet_Membership_UpdateUser | 更新用户的aspnet_users表的LastActivityDate,e-mail,注释,approved字段和aspnet_Membership表的最后登录时间。 | 
| aspnet_Membership_UpdateUserInfo | 更新帐户锁定时间在aspnet_users和aspnet_Membership表,Used in conjunction with provider methods that track bad password and bad password-answer attempts. 
 | 
| aspnet_Users_CreateUser | 调用aspnet_Membership_CreateUser增加一个到用户到aspnet_users表。 
 | 
| aspnet_Users_DeleteUser | 删除一个用户 从aspnet_membership已经一些关联表里包括aspnet_users. | 
4.创建用户
SqlMembershipProvider.CreateUser通过调用aspnet_Membership_CreateUser储存过程创建membership用户。SqlMembershipProvider.CreateUser在调用存储过程之前还会对用户的输入参数做一些校验,包括密码等。
创建用户的流程
a. 调用aspnet_Applications_CreateApplication,存储过程,转换一个ApplicationName成Application ID.如果在aspnet_Applications表中已经存在这个Application ID就返回存在Application ID,如果表中不存在,在aspnet_Applications表中新增加一条记录并返回这个Application ID.
b. 调用aspnet_Users_CreateUser在aspnet_Users表中添加一条新记录
c. 做一个验证对email地址和原来已经注册的用户。
d. 使用当前的时间来更新一下aspnet_Users表的LastActityDate字段。
e. 插入一条新记录到aspnet_Membership表。
aspnet_Membership_CreateUser提供了所有的这些步骤,并使用事务来保证数据库更新的完整性。
5.删除用户
程序通过调用Membership.DeleteUser来实现删除membership用户的功能。
Membership.DeleteUser调用默认的membership提供者的DeleteUser的函数,而这个函数有两个输入值,一个是用户名 另外一个参数(deleteAllRelatedData)是bool值,这个bool值表示是否要删除这个用户的一些关联信息,包括role data, profile data和 Web Parts personalization data。
DeleteUser还可以实现一个其他的功能就是 把用户名输入Request.AnonymousID,而参数deleteAllRelatedData设置为true,这样可以删除匿名用户在数据库的aspnet_Profile和aspnet_Users表中保存的记录。
6.验证membership用户
程序通用调用Membership.ValidateUser来实现用户的验证,返回值是一个bool值,包括用户名密码是否正确。
验证的流程
a. 通过调用存储过程 aspnet_Membership_GetPasswordWithFormat得到用户的密码,如果是加密的返回的是加密的字串。
b. 使用相同的加密码方法加密输入的密码。
c. 比较两个密码。
d. 如果密码匹配 会触发一个AuditMembershipAuthenticationSuccess的Web 事件,同时记录一个成功登陆的纪录,并返回true
e. 如果密码不匹配,会触发一个 AuditMembershipAuthenticationFailure的web 事件,返回false,同时还会调用aspnet_Membership_UpdateUserInfo储存过程做记录,如果记录发现已经达到限制用户登录的条件,还会锁住此用户。
7.密码保护
为了安全,密码保存在数据库中一般不用明文,SqlMembershipProvider提供了几种不同保存密码的方法,我们可以通过设置PasswordFormat属性来指定不同的保存方法。
a. MembershipPasswordFormat.Clear使用明文保存
b. MembershipPasswordFormat.Hashed 默认参数,会使用.Net框架的RNGCryptoServiceProvider类来对密码和密码问题进行Hash计算保存。
c. MembershipPasswordFormat.Encrypted对密码和密码问题进行加密。SqlMembershipProvider使用的是对称密钥加密方法。加密的密钥保存在<machineKey>配置字段里
为了增加一些额外的安全保护,SqlMembershipProvider还提供了MinRequiredPasswordLength,MinRequiredNonAlphanumericCharacters,PasswordStrengthRegularExpression三个属性来加强保护,根据字面意思应该是密码最小长度,最少特殊字符数,密码正则表达式。
8.帐户锁定
为了抵御穷举密码 猜密码的攻击,SqlMembershipProvider提供了一个自动锁定用户的机制,当一个帐户在一段时间内连续登录失败超过一定次数后,这个用户将被锁定,SqlMembershipProvider的MaxInvalidPasswordAttempts和PasswordAttemptWindow属性,默认MaxInvalidPasswordAttempts=5次,PasswordAttemptWindow=10分钟。当数据表里的IsLockedOut=1时用户就被锁定了。
对Membership Provider等Provider微软提供了源代码,但这些源代码和.Net框架里包含的是有区别的,主要是为了能让提供的源代码能让用户独立的编译和运行。
今天这篇文章写的太长,自己都有点晕了,有错误的地方多包涵。