[WCF权限控制]通过扩展自行实现服务授权
来源:互联网 发布:网络上吃辣条什么意思 编辑:程序博客网 时间:2024/05/17 03:28
其实针对安全主体的授权实现的原理很简单,原则上讲,只要你能在服务操作执行之前能够根据本认证的用户正确设置当前的安全主体就可以了。如果你了解WCF的整个运行时框架结构,你会马上想到用于授权的安全主体初始化可以通过自定义CallContextInitializer来实现。[源代码从这里下载]
目录:
CallContextInitializer简介
步骤一、自定义CallContextInitializer
步骤二、创建服务行为
步骤三、使用服务行为进行授权
CallContextInitializer简介
对于WCF的整个运行时框架来说,CallContextInitializer是一个重要的对象。一个运行时服务操作(DispatchOperation)具有一个CallContextInitializer列表。而每一个CallContextInitializer实现ICallContextInitializer接口。如下面的代码片断所示,ICallContextInitializer具有两个方法BeforeInvoke和AfterInvoke。它们分别在操作方法之前前后进行调用上下文的初始化和清理操作。那么我么就可以自定义CallContextInitializer,在BeforeInvoke中初始化当前的安全主体。
1: public interface ICallContextInitializer
2: {
3: void AfterInvoke(object correlationState);
4: object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message);
5: }
步骤一、自定义CallContextInitializer
我们授权自定义一个抽象的CallContextInitializer,起名为AuthorizationCallContextInitializerBase。下面的代码片断给出了AuthorizationCallContextInitializerBase的整个定义。AuthorizationCallContextInitializerBase具有一个抽象的方法GetPrincipal用于根据当前的安全上下文信息创建安全主体。该方法会在BeforeInvoke方法被调用,返回值被设置成当前线程的安全主体。为了让服务操作执行之后当前线程的上下文恢复到执行前的状态,在BeforeInvoke方法中当前的安全主体被保存下来,并传递给AfterInvoke方法中恢复当前线程的原来的安全主体。
1: public abstract class AuthorizationCallContextInitializerBase: ICallContextInitializer
2: {
3: public void AfterInvoke(object correlationState)
4: {
5: IPrincipal principal = correlationState as IPrincipal;
6: if (null != principal)
7: {
8: Thread.CurrentPrincipal = principal;
9: }
10: }
11: public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
12: {
13: var originalPrincipal = Thread.CurrentPrincipal;
14: Thread.CurrentPrincipal = this.GetPrincipal(ServiceSecurityContext.Current);
15: return originalPrincipal;
16: }
17: protected abstract IPrincipal GetPrincipal(ServiceSecurityContext serviceSecurityContext);
18: }
基于两种安全主体权限模式,我们创建了两个具体的CallContextInitializer。第一个为基于Windows用户组的WindowsAuthorizationCallContextInitializer。WindowsAuthorizationCallContextInitializer定义如下,它继承了AuthorizationCallContextInitializerBase,在实现的抽象方法GetPrincipal中根据当前ServiceSecurityContext的WindowsIdentity属性创建WindowsPrincipal。
1: public class WindowsAuthorizationCallContextInitializer:AuthorizationCallContextInitializerBase
2: {
3: protected override IPrincipal GetPrincipal(ServiceSecurityContext serviceSecurityContext)
4: {
5: WindowsIdentity identity = serviceSecurityContext.WindowsIdentity;
6: if (null == identity)
7: {
8: identity =WindowsIdentity.GetAnonymous();
9: }
10: return new WindowsPrincipal(identity);
11: }
12: }
而基于ASP.NET Roles安全主体权限模式的安全主体初始化实现在如下所示的AspRoleAuthorizationCallContextInitializer类中。AspRoleAuthorizationCallContextInitializer具有一个RoleProvider属性,表示用于获取当前用户角色列表的RoleProvider,该属性在构造函数中被初始化。在实现的GetPrincipal抽象方法中,借助于RoleProvider获取基于当前用户的所有角色,并创建GenericPrincipal。
1: public class AspRoleAuthorizationCallContextInitializer : AuthorizationCallContextInitializerBase
2: {
3: public RoleProvider RoleProvider { get; private set; }
4: public AspRoleAuthorizationCallContextInitializer(RoleProvider roleProvider)
5: {
6: this.RoleProvider = roleProvider;
7: }
8: protected override IPrincipal GetPrincipal(ServiceSecurityContext serviceSecurityContext)
9: {
10: var userName = serviceSecurityContext.PrimaryIdentity.Name;
11: var identity = new GenericIdentity(userName);
12: var roles = this.RoleProvider.GetRolesForUser(userName);
13: return new GenericPrincipal(identity, roles);
14: }
15: }
步骤二、创建服务行为
现在,用户进行安全主体初始化的两个具体的CallContextInitializer已经创建完成,现在需要做的工作就是将其应用到WCF的运行时框架体系之中。为此,我们创建了如下一个服务行为ServiceAuthorizationBehaviorAttribute。ServiceAuthorizationBehaviorAttribute是一个自定义特性,并实现了IServiceBehavior接口。它具有两个两个属性:PrincipalPermissionMode和CallContextInitializer。前者在构造函数中指定,我们根据该参数决定具体创建的CallContextInitializer类型,是WindowsAuthorizationCallContextInitializer还是AspRoleAuthorizationCallContextInitializer。而构造函数中具有一个可选的参数roleProviderName表示采用的RoleProvider配置名称。
1: [AttributeUsage( AttributeTargets.Class)]
2: public class ServiceAuthorizationBehaviorAttribute: Attribute, IServiceBehavior
3: {
4: public PrincipalPermissionMode PrincipalPermissionMode { get; private set; }
5: public ICallContextInitializer CallContextInitializer { get; private set; }
6:
7: public ServiceAuthorizationBehaviorAttribute(PrincipalPermissionMode principalPermissionMode, string roleProviderName = "")
8: {
9: switch (principalPermissionMode)
10: {
11: case PrincipalPermissionMode.UseWindowsGroups:
12: {
13: this.CallContextInitializer = new WindowsAuthorizationCallContextInitializer();
14: break;
15: }
16: case PrincipalPermissionMode.UseAspNetRoles:
17: {
18: if (string.IsNullOrEmpty(roleProviderName))
19: {
20: this.CallContextInitializer = new AspRoleAuthorizationCallContextInitializer(Roles.Provider);
21: }
22: else
23: {
24: this.CallContextInitializer = new AspRoleAuthorizationCallContextInitializer(Roles.Providers[roleProviderName]);
25: }
26: break;
27: }
28: case PrincipalPermissionMode.Custom:
29: {
30: throw new ArgumentException("只有UseWindowsGroups和UseAspNetRoles模式被支持!");
31: }
32: }
33: }
34:
35: public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
36:
37: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
38: {
39: if (null == this.CallContextInitializer)
40: {
41: return;
42: }
43:
44: foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
45: {
46: foreach (EndpointDispatcher endpoint in channelDispatcher.Endpoints)
47: {
48: foreach (DispatchOperation operation in endpoint.DispatchRuntime.Operations)
49: {
50: operation.CallContextInitializers.Add(this.CallContextInitializer);
51: }
52: }
53: }
54: }
55: public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
56: }
CallContextInitializer的注册实现在ApplyDispatchBehavior方法中,逻辑很简单:遍历所有信道分发器(ChannelDispatcher),每个信道分发器的所有终结点分发器(EndpointDispatcher),以及每个终结点分发器对应的分发运行时(DispatchRuntime)的所有运行时操作(DispatchOperation)。最后将初始化的CallContextInitializer添加到操作的CallContextInitializer列表中。
步骤三、使用服务行为进行授权
由于上面定义的服务行为ServiceAuthorizationBehaviorAttribute是一个自定义特性,所以我们可以直接将其应用到服务类型上。我们直接采用《基于Windows用户组的授权方式[下篇]》的例子。如下所示,在服务类型CalculatorService上应用了ServiceAuthorizationBehaviorAttribute特性,并采用了UseWindowsGroups安全主体权限模式。
1: [ServiceAuthorizationBehavior(PrincipalPermissionMode.UseWindowsGroups)]
2: public class CalculatorService : ICalculator
3: {
4: [PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
5: public double Add(double x, double y)
6: {
7: return x + y;
8: }
9: }
为了证明我们自定义的服务行为也能和ServiceAuthorizationBehavior一样实现正确的授权,我们需要将ServiceAuthorizationBehavior的授权功能关闭。为此我们修正了服务端的配置,将ServiceAuthorizationBehavior的PrincipalPermissionMode设置为None。
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <services>
5: <service name="Artech.WcfServices.Services.CalculatorService" behaviorConfiguration="disableAuthorization">
6: <endpoint address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" contract="Artech.WcfServices.Contracts.ICalculator"/>
7: </service>
8: </services>
9: <behaviors>
10: <serviceBehaviors>
11: <behavior name="disableAuthorization">
12: <serviceAuthorization principalPermissionMode="None"/>
13: </behavior>
14: </serviceBehaviors>
15: </behaviors>
16: </system.serviceModel>
17: </configuration>
而客户端的服务调用程序中,依然是分别以Foo和Bar(Foo具有管理员权限)的名义进行两次服服务调用。由于两个Windows帐号权限的不同,同样只有第一个服务调用能够成功,这反映在最终的执行结果中。客户端程序:
1: ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService");
2: NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;
3: credential.UserName = "Foo";
4: credential.Password = "Password";
5: ICalculator calculator = channelFactory.CreateChannel();
6: Invoke(calculator);
7:
8: channelFactory = new ChannelFactory<ICalculator>("calculatorService");
9: credential = channelFactory.Credentials.Windows.ClientCredential;
10: credential.UserName = "Bar";
11: credential.Password = "Password";
12: calculator = channelFactory.CreateChannel();
13: Invoke(calculator);
输出结果:
1: 服务调用成功...
2: 服务调用失败...
- [WCF权限控制]通过扩展自行实现服务授权
- [WCF权限控制]ASP.NET Roles授权[上篇]
- [WCF权限控制]ASP.NET Roles授权[下篇]
- [WCF权限控制]基于Windows用户组的授权方式
- 通过WCF扩展实现消息压缩
- 通过代理实现权限控制
- 通过WCF 服务 使用 EntityFramework5.0 CodeFirst方式 通过 DTO Automapper 更新实体的并发控制
- Hadoop服务层授权控制
- Hadoop服务层授权控制
- WCF实现REST服务
- 深入剖析授权在WCF中的实现
- 通过Java实现一种常用的权限控制算法
- 通过配置Sitemap实现对左侧菜单的权限控制
- CodeIgniter通过hook的方式实现简单的权限控制
- struts2通过拦截器,实现权限控制功能
- CodeIgniter通过hook的方式实现简单的权限控制
- 【WCF】利用WCF实现上传下载文件服务
- 【WCF】利用WCF实现上传下载文件服务
- Windows Phone 7 入门指南(Windows Phone 8已经发布,不过还有一些借鉴意义)
- Windows Phone 7/8 下你会用的到的3个dll(针对中国的开发者,地图纠偏/GB18030解码/图片缓存)
- 【Visual C++】游戏开发笔记三十八 浅墨DirectX提高班之六 携手迈向三维世界:四大变换展身手
- [WCF权限控制]基于Windows用户组的授权方式
- WIN7虚拟机VMWare安装Mac OS X安装时五国问题
- [WCF权限控制]通过扩展自行实现服务授权
- codeforces 157 div1(完全)
- Entity Framework 4.1 之一 : 基础
- Entity Framework 4.1 之二 : 覆盖默认的约定
- Entity Framework 4.1 之三 : 贪婪加载和延迟加载
- 最近碰到几个c++问题
- Entity Framework 4.1 之四:复杂类型
- Entity Framework 4.1 之五:多对多的关系
- Entity Framework 4.1 之六:乐观并发