关于membership实用性讨论

          第一次接触membership,被它的强大的功能,和便捷的使用所吸引。但是再近一步理解它,便不觉得它好了,首先,因为vs中它是默认使用sql server 2005 express的,如果想使用其他数据库该怎么办?其次,它所提供的用户信息也很少,如果要添加自定的信息应该怎样扩展?
        asp.net 2.0已经出来这么久了,我想应该不少人有这样的疑惑了吧!就用google搜索看是否有同我“志同道合”的同志,先是找到这么一段:
         
           最近看了一下asp.net2的用户管理模型,个人感觉他的确提供了很集成化的功能,对于一般的应用可以很傻瓜式的达到要求。但是在使用中也有以下几点疑问:

           1、无论我怎么配置,用户数据还是存储在app_data文件夹aspnetdb中,怎么让他存储在server的数据库中呢,比如我在server的myappDB数据库中也同样构造了aspnetdb中的表和存储过程?

           2、如果我要在现有用户表上扩展功能,我还能使用MemberShip类实现吗?比如,我要记录用户的登录次数(目前aspnetdb用户表似乎没有这个),或者说每个用户都有一个账户余额,并且在创建用户的时候将为每个用户给定一个可能不同的初始余额,在登录的时候将会根据余额来判断用户能否登录成功。那么这个时候MeberShip.CreateUser(),Login控件等还有用吗,或者说怎么最大化的使用原有的方法。同时,MemberShip.DeleteUser似乎是物理删除数据(不是用IsDeleted字段),这个在应用中好像一般不是这样吧。

          上面的疑问主要是vs2005新的Login系列控件的实用性问题,欢迎大家讨论并给出一点建议和思路。
============================

上面的帖子是我这几天一直存在的疑问,虽然我也知道要用继承自MembershipProvider的自定义类来解决这个问题,而且我也仔细的看了msdn的多篇相关文档,以及vs帮助中的《如何:成员资格提供程序实现示例》,但看过这些后我都没有找到解决我的问题的关键所在:

MembershipUser类是基于aspnetdb中的aspnet_Membership表的结构的(似乎可以这么说,MembershipUser类就是aspnet_Membership表的 Model),而基本上所有的Membership类以及MembershipProvider类的方法和字段都与MembershipUser有关。由于这个关系,我在自定义MembershipProvider的时候就会碰到这样的问题,MembershipProvider.GetUser是返回MembershipUser的,而事实上,我的自定义User表结构可能和aspnet_Membership表不同(比如加了n个自定义字段),这时我却无法在返回的结果中反映这个变化,得到这些信息,因为MembershipUser没有对应的字段。这个问题的存在就让我感觉到我的大部分的所谓的自定义MembershipProvider就是换汤不换药,徒劳而已。

希望得到高手指点。。也欢迎大家讨论。

           关于使用其他数据库有人给出了这样的答复:

 第一个问题:2005下的membershipProvider默认调用的是SQLExpress数据库,这个在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config下面的machine.config里面可以看到,如果你像使用SQLServer2k或者2k5,运行:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regSQL.exe程序,里面更改默认数据库,然后在项目的web.config中重写配置文件
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="LocalSqlServer"
requiresQuestionAndAnswer="false" requiresUniqueEmail="true"
passwordFormat="Hashed" minRequiredNonalphanumericCharacters="0"
minRequiredPasswordLength="3" />
</providers>
</membership>
<connectionStrings>
<add name="LocalSqlServer"
connectionString="user id=sa;password=seman;initial Catalog=Data;Data Source=(local)"
providerName="System.Data.SqlClient" />
</connectionStrings>

具体的ConnectionString根据自己的情况配置
(原文地址http://www.kaifz.com/asp-net2-0-MembershipProvider-t48368.htm

       又在天极网找到这样一篇文章:

 ASP.NET 2.0中新增的最佳功能之一是新的成员身份服务,它提供了用于创建和管理用户帐户的易于使用的API。ASP.NET 1.x大规模引入了窗体身份验证,但仍然要求您编写相当数量的代码来执行实际操作中的窗体身份验证。成员身份服务填补了ASP.NET 1.x窗体身份验证服务的不足,并且使实现窗体身份验证变得比以前简单得多。

  成员身份API通过两个新的类公开:Membership和MembershipUser。前者包含了用于创建用户、验证用户以及完成其他工作的静态方法。MembershipUser代表单个用户,它包含了用于检索和更改密码、获取上次登录日期以及完成类似工作的方法和属性。通过这两个新的类,我们可以不用写一行代码,方便得完成对用户的管理。 但是在实际开发过程中,绝对不能满足我们日常开发的需要。经过日常项目的开发和网络上资料的搜索,现将其一一列出:

  一、默认的各类的数据库是使用sql express的,而我们在实际开发中往往使用sql sever 2000或者sql server 2005,这时就需要我们修改数据库的类型。

  微软给我们提供了一个Aspnet_regsql的命令来修改默认数据库。打开 Visual Studio 2005 命令提示,输入aspnet_regsql,按照提示一步一步进行即可。

  此时打开数据库,可以发现多处来了一系列"aspnet_"开头的存储过程,这就是我们使用membership所必需的存储过程。

  此时打开IIS,[属性] →[ASP.NET] →[编辑配置]:

  [常规],连接参数LocalSqlServer按照普通的sql连接字符串格式。

  [身份验证],模式为Forms,管理提供程序的minRequiredNonalphanumericCharacters为0,这时就可以去掉默认变态的必需需要输入字母,数字等组合的密码安全了。此步也可修改密码最低长度和最大长度等等。

  经过此步骤,系统会自动在web.config中配置好了我们所需的规则。很方便,修改web.config以后都可以通过这种图形化工具来了。

  二、由于自带的login控件和membership类,只提供了简单的用户信息录入,不能满足我们项目的需要。例如:我们要用户注册的时候同时输入QQ号码,电话号码,家庭地址。那么默认的是没有办法解决的。我这里给出两种解决方案。我分别用在了不同的项目中。优缺点大家自行判断。

  1、使用profile。此类方法网上教程已经很多。不在出重复叙述,免去赚稿费的嫌疑:)。这里只是给出网上没有的部分说明。
由于membership只能列出来指定组的用户名,而不能列出其他的详细信息,我们实际使用中,往往需要对组中的其它信息进行同时修改。我采用的是自行构造datatable的方法。见代码:

public static DataTable listuser(string userRoles)//列出指定组的用户信息
{
 string[] users = Roles.GetUsersInRole(userRoles);
 //列出指定组下的用户
 DataTable dt = new DataTable();
 dt.Columns.Add("username", System.Type.GetType("System.String"));
 dt.Columns.Add("QQ", System.Type.GetType("System.String"));
 dt.Columns.Add("phone", System.Type.GetType("System.String"));
 dt.Columns.Add("address", System.Type.GetType("System.String"));
 dt.Columns.Add("email", System.Type.GetType("System.String"));
 //以上构造一个数据表
 foreach (string i in users)
 {
  DataRow dr = dt.NewRow();
  MembershipUser mu = Membership.GetUser(i);
  得到用户基本信息
  ProfileCommon p = Profile.GetProfile(i); //得到用户的profile信息
  dr[0] = mu. username;
  dr[1] = p. QQ;
  //profile是强类型,可以很方便的通过感知来添加。
  dr[2] = p. phone;
  dr[3] = p. address;
  dr[4] = mu. email;
  dt.Rows.Add(dr);
  dt.AcceptChanges();
 }
 return dt;
}
public static void deleteuser(string username)/删除指定用户
{
 Membership.DeleteUser(username);
 //系统会自动删除profile下的指定用户的信息
}
public static void updateuser(string username)/更新指定用户
{
 ProfileCommon p = Profile.GetProfile(i);
 //得到用户的profile信息
 p. phone="电话";
 p. address="地址;
 p. QQ="QQ号码";
 p.Save();
 //保存所作修改。
}


  2、自定义一个membershipinfo表格,同membership系统标关联起来。自己编写SQL语句来进行查询,修改等功能。

列出指定组的用户
select * from aspnet_membership inner join aspnet_users on
aspnet_membership.userid=aspnet_users.userid left join memberinfo on aspnet_membership.userid=memberinfo.userid
where aspnet_membership.userid=(select userid from aspnet_usersinroles inner join
aspnet_roles on aspnet_usersinroles.roleid=aspnet_roles.roleid where rolename='admin')

  
  删除、修改等功能比较简单,这里就不作叙述。可以采用membership的createuser方法建立,然后再用sql语句写入memberinfo表。

  对于建立用户,我们可以采取扩展CreateUserWizard控件,或者自行写登陆界面。
1、采取扩展CreateUserWizard控件,我们可以使用它的模版列,此时需要注意的是:用户名,密码,提示问题,提示问题答案,Email,他们的ID一定要分别是username,Password,Question,Answer,Email否则会出错,而且此时验证控件均不能使用。怀疑是IDE的一个Bug。

  如下所示,我们定义好的样式应当是:

<WizardSteps>
<asp:CreateUserWizardStep runat="server"> 自定义代码部分<ContentTemplate>
</ContentTemplate>
</asp:CreateUserWizardStep>
</WizardSteps>
代码部分:
protected void CreateUserWizard1_CreatedUser(object sender, EventArgs e)
{
 //由于系统会自动给们建立基本的信息表,所以我们只需要对profile或者membershipinfo标进行修改即可。   
 Roles.AddUserToRole(CreateUserWizard1.UserName, "shop"); 
 //添加用户到相应的组
 ProfileCommon p = (ProfileCommon)ProfileCommon.Create(CreateUserWizard1.UserName, true);
 p. QQ = ((TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("QQ")).Text.Trim();
 p.address= ((TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("address")).Text.Trim();   
 p.phone = ((TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("phone")).Text.Trim(); p.Save();//保存所作修改
}


  2、采取自己写UI,个人推荐使用这种方法。灵活性比较大,而且可以使用2005强大的验证控件。

  页面部分比较简单略过不再陈述。下面详细介绍一下代码部分。

  此时我们需要使用membership的CreateUser()方法。语法我们就不再介绍。他会根据建立用户的结果返回成一个MembershipCreateStatus枚举类,它详细的包含了所有建立用户不成功的错误信息。我们只需要根据他的值,就可以返回给界面相应的提示,如:用户名已经存在,电子邮件已经存在等等。

  对于自定义用户信息部分我们仍然可以采取profile或者自定义membershipinfo表的方法。

  列出profile建立用户的方法。SQL语句比较简单,略过不写。

protected void Button1_Click(object sender, EventArgs e)
{
 MembershipCreateStatus status;
 MembershipUser newUser = Membership.CreateUser(username.Text.Trim(), Password.Text.Trim(),  Email.Text.Trim(), Question.Text.Trim(), Answer.Text.Trim(), true, out status);
 //使用membership建立用户,并把建立结果返回给MembershipCreateStatus

  if (newUser == null)//没有新用户,则意味着出错。
  {
  GetErrorMessage(status);
  //调用GetErrorMessage函数,返回详细错误信息
  } else {
   Roles.AddUserToRole(newUser.UserName, "jiancai");
   //添加用户到相应组 ProfileCommon
   p = (ProfileCommon)ProfileCommon.Create(newUser.UserName, true);
   p.QQ = QQ.Text.Trim();
   p.address= address.Text.Trim();
   p.phone= phone.Text.Trim();
   p.Save();
  }
 }
public void GetErrorMessage(MembershipCreateStatus status)
{
 switch (status)
  {
   case MembershipCreateStatus.DuplicateUserName: DisplayAlert("当前用户已经存在,请重新选择");
    break;
    //其余各种错误信息,请查看MSDN的MembershipCreateStatus枚举类。
   default: DisplayAlert("注册00000000用户失败,请检查您的用户名,密码等信息");
    break;
  }
}


  个人见解:采取profile的方法,比较方便,由于profile 是强类型,可以通过智能感知功能减少代码的输入量。采取自定义数据表的方法,需要输入大量的sql语句,但是查询速度比较快,性能比较强,由于Roles.GetUsersInRole()方法无法分页读取数据,只能一次性读出来所有数据,而自写SQL 语句可以很方便的根分页结合起来。随着用户量的增多,故不推荐profile方法。

三、密码问题。

  个人觉得密码是一个比较头疼的问题。我们在实际开发中总是需要admin组有对用户进行密码修改的权限。Membership提供的修改密码方法只能在已经知道密码提示答案的时候才能修改。而admin组根本不可能知道用户的密码提示答案的。这里有点好笑。难道是中西方文化差异?

  列出个人对membership密码研究的一些心得。

  大家都知道machine.config和web.config,如果两者发生冲突,那么以web.config优先。

  默认状态下,membership是采用SHA1的方法进行加密,然后采取一种机制,与passwordsalt进行再次加密,最后形成数据库中显示的密码。可见随着MD5加密的破解,微软对密码的安全也煞费苦心。

  有的朋友不喜欢默认的SHA1加密形式,我们只需要在web.config中设置以下代码来覆盖machine.config中对密码的设置就好了:

<machineKey
validationKey="AutoGenerate,IsolateApps"
decryptionKey="AutoGenerate,IsolateApps"
decryption="Auto"
validation="MD5/SHA1/Clear" />


  其中:

  Clear - 密码以明文形式存储。用户密码可与此值直接比较,而不需要进行进一步的转换。

  MD5 -使用消息摘要 5 (MD5) 哈希摘要存储密码。为了验证凭据,将使用 MD5 算法对用户密码进行哈希运算并将计算出来的值与存储的值进行比较。使用此值时,从不存储或比较明文密码。此算法的性能比 SHA1 好。

  SHA1 -使用 SHA1 哈希摘要存储密码。为了验证凭据,将使用 SHA1 算法对用户密码进行哈希运算并将计算出来的值与存储的值进行比较。从不存储明文密码。使用该算法可以获得比 MD5 算法高的安全性。

  然而虽然MembershipUser提供了GetPassword 方法,但是这是后只有在加密形式设置成Clear,即密码在数据库中以明码的形式存在,才能得到密码。而ChangePassword必须要提供旧密码或者密码提示答案才可以修改。对用户管理造成了很大的不便。找了许多资料无解。顺便提带一下:微软的membership机制起源于csblog,并进行了一定的修改。

  Csblog对密码的策略:首先对用户输入的密码进行加密(SHA1或者MD5),然后根据系统自动生成的密匙再次对加密后的密码进行DES加密。不过我按照csblog 的方法进行加密的时候,形式相同,密码却不同,目前正在演就中。也欢迎大家共同讨论。

  如果passwordsalt和password固定,那么用户密码肯定一定。所以目前我采取的一个方法就是给用户重新设置成固定密码的功能。

  首先取得已知用户密码的passwordsalt和password,然后替换相应用户的passwordsalt和password字段。这时,用户的密码就已经恢复成已知的密码了。

  不可否认,membership给我们开发中创造了很大的便利,其方便的Roles功能,对于我们进行权限管理的时候提供了很好的解决方案。2005许多功能均进化自csblog这优秀的开源项目,有兴趣者可以研究csblog,以进一步的了解membership的运作机制。限于篇幅,MSDN上边有做介绍的我就不再重复叙述。只介绍MSDN上没有列出的技巧跟我在项目开发中的一些心得。 ASP.NET 2.0为使用窗体身份验证的Web站点提供了重要的安全性优势。通过提供用户配置文件储备库以及对角色的支持,窗体身份验证将走出ASP.NET内行的视野,而得以更广泛地实现。
    ( 原文地址http://dev.yesky.com/msdn/121/2307121.shtml)  

          这两篇文章一定程度的回答了我的2个疑问,但好像这样的答复使membership失去了便捷性!如果使用access数据库的项目是不是不能使用membership?

posted @ 2006-05-06 01:05  火火  阅读(3323)  评论(3编辑  收藏  举报