现在的位置: 首页 > 综合 > 正文

在asp.net 2.0中使用自定义的provider (转)

2012年02月04日 ⁄ 综合 ⁄ 共 13558字 ⁄ 字号 评论关闭

在asp.net 2.0中使用自定义的provider

在asp.net 2.0中,提供了各类的provider,有membership,role,profile等的,可以很方便地实现如角色,用户等管理,甚至不用自己写代码了。而在vs.net 2005中,默认的各类的provider是使用sql express的,有的时候,如果想使用sql sever 2000或者sql server 2005,那应该怎么办呢?下面举例子说明:

  首先,要正确使用 provider,我们要到C:\WINNT\Microsoft.NET\Framework\v2.0.50215下,运行一个

叫aspnet_regsql的工具,之后按照其步骤设置就可以了,最后会产生一个叫aspnetdb的数据库。

  接着,到C:\WINNT\Microsoft.NET\Framework\v2.0.50215\config下,打开machine.config文件,

找到如下的代码:

 <membership>

<providers>

<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="LocalSqlServer" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10" passwordStrengthRegularExpression="" />

</providers>

</membership>

注意,这里定义了membership这个provider的一些属性了,比如对密码输入的要求,对注册提示问题的要求等等,其中,留意下connectionStringName="LoacalSqlserver"这个,再查找localsqlserver,会找到如下的代码

  <connectionStrings>

    <add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" />

  </connectionStrings>

看到了吧,要改为你的sql server 2000或者sql server 2005的话,就在这里改了。

  又或者可以在web.config里改也可以的

 <connectionStrings>

<add name="MySqlProviderConnection" connectionString="server=localhost;integrated security = true;Initial Catalog=aspnetdb" />

</connectionStrings>

<system.web>

<membership defaultProvider="MySqlMembershipProvider">

<providers>

<add connectionStringName="MySqlProviderConnection" name="MySqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</membership>

</system.web>

  其他的如profile,role等provider,修改的方法也是一样的。

 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内行的视野,而得以更广泛地实现。

ASP.NET Whidbey中实现Provider

  “Whidbey”是微软工具套件的下一个版本。按照微软的计划,它将会在2004年底推出。

  Asp.Net 2.0(codename Whidbey)通过Provider模式为用户验证、角色管理等方面提供了非常强大易用的框架模型。Whidbey中提供了一个Asp.Net configuration工具,通过它可以非常容易地配置用户信息数据库,管理角色等等,再与新加入的Security控件配合,几乎不用写什么代码就能够实现用户验证和角色管理功能。关于这些控件和配置工具的具体使用,可以参考这篇文章:使用更精简的代码保证 ASP.NET 应用程序的安全

  但是在PDC Preview版本的Whidbey中,这个配置工具的功能还不是很完善。从我使用的情况来看,它目前还只能创建和连接自己的Demo用的Access数据库,不能连接SQL Server数据库进行扩展。因此,为了能够连接SQL Server,我们必须提供我们自己的Providers。这里以连接IBuySpy的Portal数据库为例来说明如何实现一个Membership Provider。

  为了搞清楚如何实现我们自己的Membership Provider,有必要先看看Whidbey默认使用的Membership Provider是如何做的。在machine.config配置文件中,Whidbey使用类似下面这样的配置实现:

<membership defaultProvider="AspNetAccessProvider" userIsOnlineTimeWindow="15" >

<providers>

<add name="AspNetSqlProvider"

 type="System.Web.Security.SqlMembershipProvider, System.Web, Version=1.2.3400.0,  Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="LocalSqlServer"

enablePasswordRetrieval="false"

enablePasswordReset="true"

requiresQuestionAndAnswer="false"

applicationName="/"

requiresUniqueEmail="false"

passwordFormat="Hashed"

description="Stores and retrieves membership data from the local Microsoft SQL Server database"

/>

<add name="AspNetAccessProvider"

type="System.Web.Security.AccessMembershipProvider, System.Web, Version=1.2.3400.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

connectionStringName="AccessFileName"

enablePasswordRetrieval="false"

enablePasswordReset="true"

requiresQuestionAndAnswer="false"

applicationName="/"

requiresUniqueEmail="false"

passwordFormat="Hashed"

description="Stores and retrieves membership data from the local Microsoft Access database file"

/>

</providers>

</membership> 

  关于这段配置文件的更详细解说,可以参考《A First Look at ASP.NET v. 2.0》。

  可以看出,Whidbey默认使用SqlMembershipProvider或者AccessMembershipProvider来进行用户验证和管理。这两个Provider实现了IProvider和IMembershipProvider接口,实际上这两个接口也是每个MembershipProvider所必需的,其中IProvider负责Provider的初始化,而IMembershipProvider则实现MembershipProvider的主要功能。它们的定义如下:

namespace System.Configuration.Provider

{

 public interface IProvider

 {

  public string Name { get; }

  public void Initialize(string name,

  System.Collections.Specialized.NameValueCollection config);

 }

}

namespace System.Web.Security

{

 public interface IMembershipProvider

 {

  public bool ChangePassword(string name, string oldPwd, string newPwd);

  public bool ChangePasswordQuestionAndAnswer(string name, string password,

string newPwdQuestion, string newPwdAnswer);

  public System.Web.Security.MembershipUser CreateUser(string username, string password, string email,out System.Web.Security.MembershipCreateStatus status);

  public bool DeleteUser(string name);

  public System.Web.Security.MembershipUserCollection GetAllUsers();

  public int GetNumberOfUsersOnline();

  public string GetPassword(string name, string answer);

  public System.Web.Security.MembershipUser GetUser(string name,bool userIsOnline);

  public string GetUserNameByEmail(string email);

  public string ResetPassword(string name, string answer);

  public void UpdateUser(System.Web.Security.MembershipUser user);

  public bool ValidateUser(string name, string password);

  public string ApplicationName {get; set;}

  public bool EnablePasswordReset { get;}

  public bool EnablePasswordRetrieval { get;}

  public bool RequiresQuestionAndAnswer { get;}

 }

  现在可以动手来实现我们自己的MembershipProvider了:

public class MyMembershipProvider : IProvider, IMembershipProvider

{

 ……

  验证功能是必需的:

public bool ValidateUser (string name, string password)

{

 string connectStr = ConfigurationSettings.ConnectionStrings["PortalData"];

 SqlConnection myConnection = new SqlConnection (connectStr);

 SqlCommand myCommand = new SqlCommand ("UserLogin", myConnection);

 myCommand.CommandType = CommandType.StoredProcedure;

 // Add Parameters to SPROC

 SqlParameter parameterEmail = new SqlParameter ("@Email", SqlDbType.NVarChar, 100);

 parameterEmail.Value = name;

 myCommand.Parameters.Add (parameterEmail);

 SqlParameter parameterPassword = new SqlParameter ("@Password", SqlDbType.NVarChar, 20);

 parameterPassword.Value = password;

 myCommand.Parameters.Add (parameterPassword);

 SqlParameter parameterUserName = new SqlParameter ("@UserName", SqlDbType.NVarChar, 100);

 parameterUserName.Direction = ParameterDirection.Output;

 myCommand.Parameters.Add (parameterUserName);  

 // Open the database connection and execute the command

 myConnection.Open ();

 myCommand.ExecuteNonQuery ();

 myConnection.Close ();

 if ((parameterUserName.Value != null) && (parameterUserName.Value != System.DBNull.Value))

  return true;

  return false;

}

 

  现在在web.config中可以这样配置connectionString了:

<connectionStrings>

<add name="BugDepotData" connectionString="Data Source=(local);Trusted_Connection=true;Database=Portal" />

</connectionStrings> 

  这样,我们自己的一个简单的MembershipProvider就基本上完成了。接下来需要配置web.config,让需要Provider服务的控件能够认识它:

<membership>

 <providers>

  <add name="MyMembershipProvider" type="MyMembershipProvider" appName="/" />

 </providers>

</membership> 

  这段设置是参考machine.config而来的,其中type属性的值是这样的字符串:

type="ProviderType, Assembly, Version, Culture, PublicKeyToken" 

  由于我们的MyMembershipProvider放在/Code目录下,并不是在单独的Assembly中,因此只需要指出ProviderType就行了。

  这样,一个具有验证功能的Provider就完成了,现在可以在页面上放一个新的Security控件,比如Login控件,并指定它的MembershipProperty为MyMembershipProvider(或者也可以设置membership的defaultProvider属性为MyMembershipProvider),打开Forms验证,试试是不是已经能够成功登陆了?

抱歉!评论已关闭.