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

WCF版的PetShop之三:实现分布式的Membership和上下文传递

2014年04月16日 ⁄ 综合 ⁄ 共 5201字 ⁄ 字号 评论关闭

通过上一篇了解了模块内基本的层次划分之后,接下来我们来聊聊PetShop中一些基本基础功能的实现,以及一些设计、架构上的应用如何同WCF进行集成。本篇讨论两个问题:实现分布式的Membership和客户端到服务端上下文(Context)的传递。

一、 如何实现用户验证

对登录用户的验证是大部分应用所必需的,对于ASP.NET来说,用户验证及帐号管理实现在成员资格(Membership)模块中。同ASP.NET的其他模块一样,微软在设计Membership的时候,为了实现更好地可扩展性,采用了策略(Strategy)设计模式:将模块相关的功能定义在被称为Provider的抽象类型中,并通过继承它提供具体的Provider。如果这些原生的Provider不能满足你的需求,你也可以通过继承该抽象的Provider,创建自定义的Provider。通过ASP.NET提供的配置,你可以很轻易地把自定义的Provider应用到你的应用之中。在一般情况下,最终的编程人员并不通过Provider调用相关的功能,而是通过一个外观(Facade)类实现对相关功能的调用。

ASP.NET成员资格模块的设计基本上可以通过下面的类图1反映出来:最终的编程人员通过外观类型(Façade Class)Membership调用成员资格相关的功能,比如用户认证、用户注册、修改密码等;Membership通过抽象类MembershipProvider提供所有的功能,至于最终的实现,则定义在一个个具体的MembershipProvider中。基于成员资格信息不同的存储方式,ASP.NET提供了两个原生的MembershipProvider:SqlMembershipProviderActiveDirectoryMembershipProvider,前者基于SQL Server数据库,后者基于AD。如果这两个MembershipProvider均不能满足需求,我们还可以自定义MembershipProvider。

clip_image002

图1 ASP.NET Membership 设计原理

我们的案例并不会部署于AD之中,所以不能使用ActiveDirectoryMembershipProvider;直接通过Web服务器进行数据库的存取又不符合上述物理部署的要求(通过应用服务器进行数据库访问),所以SqlMembershipProvider也不能为我们所用。为此需要自定义MembershipProvider,通过WCF服务调用的形式提供成员资格所有功能的实现。我们将该自定义MembershipProvider称为RemoteMembershipProvider。图2揭示了RemoteMembershipProvider实现的原理:RemoteMembershipProvider通过调用WCF服务MembershipService提供对成员资格所有功能的实现;MembershipService则通过调用Membership实现服务;最终的实现还是落在了SqlMembershipProvider这个原生的MembershipProvider上。

clip_image004

图2 RemoteMembershipProvider实现原理

1、服务契约和服务实现

首先来看看MembershipService实现的服务契约的定义。由于MembershipService最终是为RemoteMembershipProvider这个自定义MembershipProvider服务的,所以服务操作的定义是基于MembershipProvider的API定义。MembershipProvider包含两种类型的成员:属性和方法,简单起见,我们可以为MembershipProvider每一个抽象方法定义一个匹配的服务操作;而对于所有属性,完全采用服务端(应用服务器)的MembershipProvider相关属性。在RemoteMembershipProvider初始化的时候通过调用MembershipService获取所有服务端MembershipProvider的配置信息。为此,我们为MembershipProvider的所有属性定义了一个数据契约:MembershipConfigData。在PetShop中,MembershipConfigData和服务契约一起定义在Infrastructures.Service.Interface项目中。

   1: using System.Runtime.Serialization;

   2: using System.Web.Security;

   3: namespace Artech.PetShop.Infrastructures.Service.Interface

   4: {

   5:     [DataContract(Namespace = "http://www.artech.com/")]

   6:     public class MembershipConfigData

   7:     {

   8:         [DataMember]

   9:         public  string ApplicationName

  10:         { get; set; }

  11:  

  12:         [DataMember]

  13:         public bool EnablePasswordReset

  14:         { get; set; }

  15:  

  16:         [DataMember]

  17:         public bool EnablePasswordRetrieval

  18:         { get; set; }

  19:  

  20:         [DataMember]

  21:         public int MaxInvalidPasswordAttempts

  22:         { get; set; }

  23:  

  24:         [DataMember]

  25:         public int MinRequiredNonAlphanumericCharacters

  26:         { get; set; }

  27:  

  28:         [DataMember]

  29:         public int MinRequiredPasswordLength

  30:         { get; set; }

  31:  

  32:         [DataMember]

  33:         public int PasswordAttemptWindow

  34:         { get; set; }

  35:  

  36:         [DataMember]

  37:         public MembershipPasswordFormat PasswordFormat

  38:         { get; set; }

  39:  

  40:         [DataMember]

  41:         public string PasswordStrengthRegularExpression

  42:         { get; set; }

  43:  

  44:         [DataMember]

  45:         public bool RequiresQuestionAndAnswer

  46:         { get; set; }

  47:  

  48:         [DataMember]

  49:         public bool RequiresUniqueEmail

  50:         { get; set; }

  51:     }

  52: }

在服务契约中,定义了一个额外的方法GetMembershipConfigData获取服务端MembershipProvider的所有配置信息,而对于服务操作的定义,则与MembershipProvider同名抽象方法相对应。

   1: using System.ServiceModel;

   2: using System.Web.Security;

   3: namespace Artech.PetShop.Infrastructures.Service.Interface

   4: {

   5:     [ServiceContract(Namespace="http://www.artech.com/")]

   6:     public interface IMembershipService

   7:     {

   8:         [OperationContract]

   9:         bool ChangePassword(string username, string oldPassword, string newPassword);

  10:         [OperationContract]

  11:         bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer);

  12:         [OperationContract]

  13:         MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status);

  14:         [OperationContract]

  15:         bool DeleteUser(string username, bool deleteAllRelatedData);

  16:         [OperationContract]

  17:         MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords);

  18:         [OperationContract]

  19:         MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords);

  20:         [OperationContract]

  21:         MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords);

  22:         [OperationContract]

  23:         int GetNumberOfUsersOnline();

  24:         [OperationContract]

  25:         string GetPassword(string username, string answer);

  26:         [OperationContract(Name="GetUserByName")]

  27:         MembershipUser GetUser(string username, bool userIsOnline);

  28:         [OperationContract(Name="GetUserByID")]

  29:         MembershipUser GetUser(object providerUserKey, bool userIsOnline);

  30:         [OperationContract]

  31:         string GetUserNameByEmail(string email);

  32:         [OperationContract]

  33:         string ResetPassword(string username, string answer);

  34:         [OperationContract]

  35:         bool UnlockUser(string userName);

  36:         [OperationContract]

  37:         void UpdateUser(MembershipUser user);

  38:         [OperationContract]

  39:         bool ValidateUser(string username, string password);

  40:         [OperationContract]

  41:         MembershipConfigData GetMembershipConfigData();

  42:     }

  43: }

服务的实现,则异常简单,我们须要做的仅仅是通过Membership.Provider获得当前的MembershipProvider,调用同名的属性或方法即可。MembershipService定义在Infrastructures.Service中,定义如下:

   1: using System.Web.Security;

   2: using Artech.PetShop.Infrastructures.Service.Interface;

   3: namespace Artech.PetShop.Infrastructures.Service

   4: {

   5:     public class MembershipService : IMembershipService

   6:     {

   7:         #region IMembershipService Members

   8:  

   9:         public bool ChangePassword(string username, string oldPassword, string newPassword)

抱歉!评论已关闭.