实现分布式的Membership和上下文传递剖析.docx
《实现分布式的Membership和上下文传递剖析.docx》由会员分享,可在线阅读,更多相关《实现分布式的Membership和上下文传递剖析.docx(18页珍藏版)》请在冰豆网上搜索。
![实现分布式的Membership和上下文传递剖析.docx](https://file1.bdocx.com/fileroot1/2023-1/29/f2ce680c-ffaf-4674-a710-1e9bbdd04c34/f2ce680c-ffaf-4674-a710-1e9bbdd04c341.gif)
实现分布式的Membership和上下文传递剖析
实现分布式的Membership和上下文传递
通过上一篇了解了模块内基本的层次划分之后,接下来我们来聊聊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çadeClass)Membership调用成员资格相关的功能,比如用户认证、用户注册、修改密码等;Membership通过抽象类MembershipProvider提供所有的功能,至于最终的实现,则定义在一个个具体的MembershipProvider中。
基于成员资格信息不同的存储方式,ASP.NET提供了两个原生的MembershipProvider:
SqlMembershipProvider和ActiveDirectoryMembershipProvider,前者基于SQLServer数据库,后者基于AD。
如果这两个MembershipProvider均不能满足需求,我们还可以自定义MembershipProvider。
clip_image002
图1ASP.NETMembership设计原理
我们的案例并不会部署于AD之中,所以不能使用ActiveDirectoryMembershipProvider;直接通过Web服务器进行数据库的存取又不符合上述物理部署的要求(通过应用服务器进行数据库访问),所以SqlMembershipProvider也不能为我们所用。
为此需要自定义MembershipProvider,通过WCF服务调用的形式提供成员资格所有功能的实现。
我们将该自定义MembershipProvider称为RemoteMembershipProvider。
图2揭示了RemoteMembershipProvider实现的原理:
RemoteMembershipProvider通过调用WCF服务MembershipService提供对成员资格所有功能的实现;MembershipService则通过调用Membership实现服务;最终的实现还是落在了SqlMembershipProvider这个原生的MembershipProvider上。
clip_image004
图2RemoteMembershipProvider实现原理
1、服务契约和服务实现
首先来看看MembershipService实现的服务契约的定义。
由于MembershipService最终是为RemoteMembershipProvider这个自定义MembershipProvider服务的,所以服务操作的定义是基于MembershipProvider的API定义。
MembershipProvider包含两种类型的成员:
属性和方法,简单起见,我们可以为MembershipProvider每一个抽象方法定义一个匹配的服务操作;而对于所有属性,完全采用服务端(应用服务器)的MembershipProvider相关属性。
在RemoteMembershipProvider初始化的时候通过调用MembershipService获取所有服务端MembershipProvider的配置信息。
为此,我们为MembershipProvider的所有属性定义了一个数据契约:
MembershipConfigData。
在PetShop中,MembershipConfigData和服务契约一起定义在Infrastructures.Service.Interface项目中。
1:
usingSystem.Runtime.Serialization;
2:
usingSystem.Web.Security;
3:
namespaceArtech.PetShop.Infrastructures.Service.Interface
4:
{
5:
[DataContract(Namespace="
6:
publicclassMembershipConfigData
7:
{
8:
[DataMember]
9:
publicstringApplicationName
10:
{get;set;}
11:
12:
[DataMember]
13:
publicboolEnablePasswordReset
14:
{get;set;}
15:
16:
[DataMember]
17:
publicboolEnablePasswordRetrieval
18:
{get;set;}
19:
20:
[DataMember]
21:
publicintMaxInvalidPasswordAttempts
22:
{get;set;}
23:
24:
[DataMember]
25:
publicintMinRequiredNonAlphanumericCharacters
26:
{get;set;}
27:
28:
[DataMember]
29:
publicintMinRequiredPasswordLength
30:
{get;set;}
31:
32:
[DataMember]
33:
publicintPasswordAttemptWindow
34:
{get;set;}
35:
36:
[DataMember]
37:
publicMembershipPasswordFormatPasswordFormat
38:
{get;set;}
39:
40:
[DataMember]
41:
publicstringPasswordStrengthRegularExpression
42:
{get;set;}
43:
44:
[DataMember]
45:
publicboolRequiresQuestionAndAnswer
46:
{get;set;}
47:
48:
[DataMember]
49:
publicboolRequiresUniqueEmail
50:
{get;set;}
51:
}
52:
}
在服务契约中,定义了一个额外的方法GetMembershipConfigData获取服务端MembershipProvider的所有配置信息,而对于服务操作的定义,则与MembershipProvider同名抽象方法相对应。
1:
usingSystem.ServiceModel;
2:
usingSystem.Web.Security;
3:
namespaceArtech.PetShop.Infrastructures.Service.Interface
4:
{
5:
[ServiceContract(Namespace="
6:
publicinterfaceIMembershipService
7:
{
8:
[OperationContract]
9:
boolChangePassword(stringusername,stringoldPassword,stringnewPassword);
10:
[OperationContract]
11:
boolChangePasswordQuestionAndAnswer(stringusername,stringpassword,stringnewPasswordQuestion,stringnewPasswordAnswer);
12:
[OperationContract]
13:
MembershipUserCreateUser(stringusername,stringpassword,stringemail,stringpasswordQuestion,stringpasswordAnswer,boolisApproved,objectproviderUserKey,outMembershipCreateStatusstatus);
14:
[OperationContract]
15:
boolDeleteUser(stringusername,booldeleteAllRelatedData);
16:
[OperationContract]
17:
MembershipUserCollectionFindUsersByEmail(stringemailToMatch,intpageIndex,intpageSize,outinttotalRecords);
18:
[OperationContract]
19:
MembershipUserCollectionFindUsersByName(stringusernameToMatch,intpageIndex,intpageSize,outinttotalRecords);
20:
[OperationContract]
21:
MembershipUserCollectionGetAllUsers(intpageIndex,intpageSize,outinttotalRecords);
22:
[OperationContract]
23:
intGetNumberOfUsersOnline();
24:
[OperationContract]
25:
stringGetPassword(stringusername,stringanswer);
26:
[OperationContract(Name="GetUserByName")]
27:
MembershipUserGetUser(stringusername,booluserIsOnline);
28:
[OperationContract(Name="GetUserByID")]
29:
MembershipUserGetUser(objectproviderUserKey,booluserIsOnline);
30:
[OperationContract]
31:
stringGetUserNameByEmail(stringemail);
32:
[OperationContract]
33:
stringResetPassword(stringusername,stringanswer);
34:
[OperationContract]
35:
boolUnlockUser(stringuserName);
36:
[OperationContract]
37:
voidUpdateUser(MembershipUseruser);
38:
[OperationContract]
39:
boolValidateUser(stringusername,stringpassword);
40:
[OperationContract]
41:
MembershipConfigDataGetMembershipConfigData();
42:
}
43:
}
服务的实现,则异常简单,我们须要做的仅仅是通过Membership.Provider获得当前的MembershipProvider,调用同名的属性或方法即可。
MembershipService定义在Infrastructures.Service中,定义如下:
1:
usingSystem.Web.Security;
2:
usingArtech.PetShop.Infrastructures.Service.Interface;
3:
namespaceArtech.PetShop.Infrastructures.Service
4:
{
5:
publicclassMembershipService:
IMembershipService
6:
{
7:
#regionIMembershipServiceMembers
8:
9:
publicboolChangePassword(stringusername,stringoldPassword,stringnewPassword)
10:
{
11:
returnMembership.Provider.ChangePassword(username,oldPassword,newPassword);
12:
}
13:
14:
publicboolChangePasswordQuestionAndAnswer(stringusername,stringpassword,stringnewPasswordQuestion,stringnewPasswordAnswer)
15:
{
16:
returnMembership.Provider.ChangePasswordQuestionAndAnswer(username,password,newPasswordQuestion,newPasswordAnswer);
17:
}
18:
//其他成员
19:
publicMembershipConfigDataGetMembershipConfigData()
20:
{
21:
returnnewMembershipConfigData
22:
{
23:
ApplicationName=Membership.Provider.ApplicationName,
24:
EnablePasswordReset=Membership.Provider.EnablePasswordReset,
25:
EnablePasswordRetrieval=Membership.Provider.EnablePasswordRetrieval,
26:
MaxInvalidPasswordAttempts=Membership.Provider.MaxInvalidPasswordAttempts,
27:
MinRequiredNonAlphanumericCharacters=Membership.Provider.MinRequiredNonAlphanumericCharacters,
28:
MinRequiredPasswordLength=Membership.Provider.MinRequiredPasswordLength,
29:
PasswordAttemptWindow=Membership.Provider.PasswordAttemptWindow,
30:
PasswordFormat=Membership.Provider.PasswordFormat,
31:
PasswordStrengthRegularExpression=Membership.Provider.PasswordStrengthRegularExpression,
32:
RequiresQuestionAndAnswer=Membership.Provider.RequiresQuestionAndAnswer,
33:
RequiresUniqueEmail=Membership.Provider.RequiresUniqueEmail
34:
};
35:
}
36:
37:
#endregion
38:
}
39:
}
2、RemoteMembershipProvider的实现
由于RemoteMembershipProvider完全通过调用WCF服务的方式提供对所有成员资格功能的实现,所以进行RemoteMembershipProvider配置时,配置相应的终结点就可以了。
1:
xmlversion="1.0"?
>
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
//localhost/PetShop/Infrastructures/MembershipService.svc"behaviorConfiguration="petShopBehavior"binding="ws2007HttpBinding"contract="Artech.PetShop.Infrastructures.Service.Interface.IMembershipService"name="membershipservice"/>
13:
14:
15:
在RemoteMembershipProvider中,通过Initialize方法获取配置的终结点名称并创建服务代理。
通过该代理调用GetMembershipConfigData操作获取服务端MembershipProvider的配置信息,并对RemoteMembershipProvider进行初始化,RemoteMembershipProvider定义如下:
1:
usingSystem.Collections.Specialized;
2:
usingSystem.Configuration;
3:
usingSystem.Linq;
4:
usingSystem.Web.Security;
5:
usingArtech.PetShop.Common;
6:
usingArtech.PetShop.Infrastructures.Service.Interface;
7:
8:
namespaceArtech.PetShop.Infrastructures
9:
{
10:
publicclassRemoteMembershipProvider:
MembershipProvider
11:
{
12:
privatebool_enablePasswordReset;
13:
privatebool_enablePasswordRetrieval;
14:
//其他字段成员
15:
16:
publicIMembershipServiceMembershipProxy
17:
{get;privateset;}
18:
19:
publicoverrideintMaxInvalidPasswordAttempts
20:
{
21:
get{returnthis._maxInvalidPasswordAttempts;}
22:
}
23:
24:
//其他属性成员
25:
publicoverridevoidInitialize(stringname,NameValueCollectionconfig)
26:
{
27:
if(!
config.AllKeys.Contains("endpoint"))
28:
{
29:
thrownewConfigurationErrorsException("Missingthemandatory\"endpoint\"configuraitonproperty.");
30:
}
31:
32:
this.MembershipProxy=ServiceProxyFactory.Create(config["endpoint"]);
33:
base.Initialize(name,config);
34:
MembershipConfigDataconfigData=this.MembershipProxy.GetMembershipConfigData();
35:
this.ApplicationName=configData.ApplicationName;
36:
this._enablePasswordReset=configData.EnablePasswordReset;
37:
this._enablePasswordRetrieval=configData.EnablePasswordRetrieval;
38:
//......
39:
}
40:
}
41:
}
对于其他抽象方法的实现,仅仅须要通过上面创建的服务代理,调用相应的服务操作即可。
注:
为了避免在服务操作调用后频繁地进行服务代理的关闭(Close)和终止(Abort)操作,我们采用基于AOP的方式实现服务的调用,将这些操作封装到一个自定义的RealProxy中,并通过ServiceProxyFactory创建该