前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[WCF权限控制]通过扩展自行实现服务授权[提供源码下载]

[WCF权限控制]通过扩展自行实现服务授权[提供源码下载]

作者头像
蒋金楠
发布于 2018-01-16 10:54:39
发布于 2018-01-16 10:54:39
7670
举报
文章被收录于专栏:大内老A大内老A

其实针对安全主体的授权实现的原理很简单,原则上讲,只要你能在服务操作执行之前能够根据本认证的用户正确设置当前的安全主体就可以了。如果你了解WCF的整个运行时框架结构,你会马上想到用于授权的安全主体初始化可以通过自定义CallContextInitializer来实现。[源代码从这里下载]

目录: CallContextInitializer简介 步骤一、自定义CallContextInitializer 步骤二、创建服务行为 步骤三、使用服务行为进行授权

CallContextInitializer简介

对于WCF的整个运行时框架来说,CallContextInitializer是一个重要的对象。一个运行时服务操作(DispatchOperation)具有一个CallContextInitializer列表。而每一个CallContextInitializer实现ICallContextInitializer接口。如下面的代码片断所示,ICallContextInitializer具有两个方法BeforeInvoke和AfterInvoke。它们分别在操作方法之前前后进行调用上下文的初始化和清理操作。那么我么就可以自定义CallContextInitializer,在BeforeInvoke中初始化当前的安全主体。

代码语言:js
AI代码解释
复制
   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方法中恢复当前线程的原来的安全主体。

代码语言:js
AI代码解释
复制
   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。

代码语言:js
AI代码解释
复制
   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。

代码语言:js
AI代码解释
复制
   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配置名称。

代码语言:js
AI代码解释
复制
   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安全主体权限模式。

代码语言:js
AI代码解释
复制
   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帐号权限的不同,同样只有第一个服务调用能够成功,这反映在最终的执行结果中。客户端程序:

代码语言:js
AI代码解释
复制
   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);

输出结果:

代码语言:js
AI代码解释
复制
   1: 服务调用成功...
   2: 服务调用失败...
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2011-07-05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
React Native组件只Image
不管在Android还是在ios原生的开发中,图片都是作为控件给出来的,在RN中也有这么一个控件(Image)。根据官网的资料,图片分为本地静态图片,网络图片和混合app资源。一下分类介绍来源官网。 静态图片资源 从0.14版本开始,React Native提供了一个统一的方式来管理iOS和Android应用中的图片。要往App中添加一个静态图片,只需把图片文件放在代码文件夹中某处,然后像下面这样去引用它: <Image source={require('./my-icon.png')} /> 图片文件
xiangzhihong
2018/02/05
1.9K0
React Native组件只Image
基于豆瓣和妹子的api用React Native写的demo for android
最近一直在学React Naitve,可以说React Native的确有他自身强大的地方,不管是运行效率还是热更新都和一般的h5有的一比,当然因为面世的时间还不算太久,版本更新又十分的快,所以坑也多,对于一般的移动开发者来说学习成本也蛮大的, 个人觉得用React Naitve做混合开发,把一些需要经常变化的模块用react native开发还是一个不错的选择。
HelloJack
2018/08/28
8890
基于豆瓣和妹子的api用React Native写的demo for android
ReactNative常用组件汇总
导航组件 react-navigation: https://github.com/react-community/react-navigation 网络请求 asios: https://github.com/mzabriskie/axios 设备信息 react-native-device-info: https://github.com/rebeccahughes/react-native-device-info 缓存使用 react-native-storage: https://github.co
磊哥
2018/05/08
1K0
React-Native 20分钟入门指南
在React-Native出现之前移动端主流的开发模式是原生开发和Hybrid开发(H5混合原生开发),Hybrid app相较于native app的优势是开发成本低开发速度快(H5页面开发跨平台,无需重新写web、android、ios代码),尽管native app在开发上需要更多时间,但却带来了更好的用户体验(页面渲染、手势操作的流畅性),也正是基于这两点Facebook在2015年推出了React-Native
conanma
2021/11/02
3.6K0
React Native 学习资源精选仓库
React Native Awesome汇集了各类react-native学习资料、工具、组件、开源App、资源下载、以及相关新闻等,只求精不求全。 如果你是一名React Native爱好者,或者有一颗热爱钻研新技术的心,喜欢分享技术干货、项目经验、以及你在React Naive学习研究或实践中的一些经验心得等等,欢迎投稿《React Native Awesome》。 如果你是一名Android、iOS、或前端开发人员,有者一颗积极进取的心,欢迎关注《React Native Awesome》。本项目汇
CrazyCodeBoy
2018/05/07
3.1K0
React Native 常用的 15 个库
本篇 React native 库列表不是从网上随便找的, 这些是我在我的应用中亲自使用的库。 这些库功能可能跟其它库也有,但经过大量研究并在我的程序中尝试后,我选择了这些库。
前端小智@大迁世界
2019/04/25
6.2K0
React Native 常用的 15 个库
React Native之Picker组件详解
Picker简介 在iOS和Android中选择器(Picker)是常见的控件之一,比如TimePickr(Android),pickerView(ios),并且这些基本控件可以实现诸如地址选择等效果。在RN开发中,系统也为我们提供Picker控件。应用如下: <Picker selectedValue={this.state.language} onValueChange={(lang) => this.setState({language: lang})}> <Picker.Item lab
xiangzhihong
2018/02/06
5K0
React Native之Picker组件详解
一个简单的ReactNative demo
本人非前端,请轻喷 ReactNative版本:0.31 github:https://github.com/X-FAN/reactnativelearn
夏洛克的猫
2018/10/18
2.1K0
一个简单的ReactNative demo
React Native控件只TextInput
TextInput是一个允许用户在应用中通过键盘输入文本的基本组件。本组件的属性提供了多种特性的配置,譬如自动完成、自动大小写、占位文字,以及多种不同的键盘类型(如纯数字键盘)等等。 比如官网最简单的写法: import React, { Component } from 'react'; import { AppRegistry, TextInput } from 'react-native'; class UselessTextInput extends Component { construct
xiangzhihong
2018/02/05
3.8K0
React Native控件只TextInput
使用react-native-tab-navigator切换页面
切换页面是app最基本功能。这个功能需要用Navigation组件实现。 RN发展太快了(v49),之前自带的Navigation组件被弃用了,如果只针对ios,还可以用NavigatorIOS 社区中也有几个不错的 https://github.com/react-community/react-navigation https://github.com/wix/react-native-navigation https://github.com/happypancake/react-native-tab-navigator 以react-native-tab-navigator为例,实现下面的tab切换效果很容易:
mafeifan
2018/09/10
2.8K0
使用react-native-tab-navigator切换页面
基础篇章:关于 React Native 之 Picker 组件的讲解
(友情提示:RN学习,从最基础的开始,大家不要嫌弃太基础,会的同学请自行略过,希望不要耽误已经会的同学的宝贵时间) 今天我们就讲Picker ,顾名思义就是选择器。用法也是相当的简单。这里我们直接就看属性吧。 Picker 的属性 onValueChange function 当选择器中的某一项被选中的时候进行回调此函数。回调时有如下两个参数: itemValue 被选中项的value属性 itemPosition 被选中项所在的索引 selectedValue any 默认选中的值,可谓字符串或者整数 s
非著名程序员
2018/02/09
1.4K0
基础篇章:关于 React Native 之 Picker 组件的讲解
那些React-Native踩过的的坑
    这几天开始边学边做新模式,也踩了不少坑,所以会记录下来--俗话说好记心不如烂笔头,何况还没有一颗好记心(-_-)。    从学React-Native开发功能模块大概5天,有些体会:1如果说按产品原型去做一样东西,那是容易的,但是这会造成很多问题,第一个是机器人一样写代码,你不会从项目整体思考,代码的质量也比较差而且不容易维护),所以决定每天写个博客,看1个小时React-native基础点。  0x01 关于Reac-Native调试命令react-native start的坑    wind
用户1148881
2018/01/15
2.1K0
那些React-Native踩过的的坑
一个上架了的React Native项目实战总结
学习 : 视频开发教程 喜欢逛GitHub的小伙伴都知道,它有个查看最热项目的功能叫trending,但这个功能只能在网页上查看, 而且在手机上浏览显示效果很不友好,而我想在地铁上,餐厅,路上等空余的时间使用它,所以我需要一款带有这个功能的App, 不仅于此,我还想要在这款App上查询GitHub上我所喜欢的项目,甚至在手机没网的时候也能看到,而且我想要我的iOS和Android手机都能使用这款App, 于是GitHub Popular便诞生了。 这个项目满足了我如下3方面的需求: 在手机App
CrazyCodeBoy
2018/05/07
1.8K0
一个上架了的React Native项目实战总结
React Native 表格组件
npm install--save react-native-data-table
forrest23
2018/08/03
1.9K0
React Native 表格组件
【React Native 安卓开发】----侧边栏的实现DrawerLayoutAndroid以及第三方框架react-native-side-menu的使用【第六篇】
做过安卓原生开发的童鞋们应该都做过侧边栏这个东西,而且对于开源框架SlidingMenu和android官方侧滑菜单DrawerLayout应该都不陌生。 那么今天也在这里给大家介绍一下React-Native中的侧滑菜单DrawerLayoutAndroid和第三方框架react-native-side-menu。
先知先觉
2019/01/21
7K0
React Native基础&入门教程:初步使用Flexbox布局
在上篇中,笔者分享了部分安装并调试React Native应用过程里的一点经验,如果还没有看过的同学请点击《React Native基础&入门教程:调试React Native应用的一小步》。 在本篇里,让我们一起来了解一下,什么是Flexbox布局,以及如何使用。 一、长度的单位 在开始任何布局之前,让我们来首先需要知道,在写React Native组件样式时,长度的不带单位的,它表示“与设备像素密度无关的逻辑像素点”。 这个怎么理解呢? 我们知道,屏幕上一个发光的最小点,对应着一个pixel(像素)点。
葡萄城控件
2018/07/03
2.1K0
React Native控件之ListView
概述 ListView作为核心组件之一,主要用于高效地显示一个可以垂直滚动的变化的数据列表。经过自定义组装,我们还可以用它实现九宫格等页面效果。 在React Native中,使用ListView组件至少需要两个属性:DataSource和renderRow。DataSource是需要渲染界面的数据源,renderRow是根据数据源的元素返回的可渲染的组件,即ListView的一行。 在React Native中,最基本的使用方式就是创建一个ListView.DataSource数据源,然后给它传递
xiangzhihong
2018/02/06
1.6K0
React Native控件之ListView
React Native 小记 - TouchableOpacity 单次点击无效
版权声明:本文为[他叫自己Mr.张]的原创文章,转载请注明出处,否则禁止转载。 https://micro.blog.csdn.net/article/details/83308510
他叫自己MR.张
2019/07/01
3K0
React Native学习笔记(三)—— 样式、布局与核心组件
React Native 有一个内置的命令行界面,你可以用它来生成一个新项目。您可以使用 Node.js 附带的 访问它,而无需全局安装任何内容。让我们创建一个名为“AwesomeProject”的新 React Native 项目:npx
张果
2023/04/12
14.8K0
React Native学习笔记(三)—— 样式、布局与核心组件
React Native 集成分享第三方登录功能分享第三方登录模块开发(Android)
在我们常用的App中经常会看到分享与第三方登录的功能,可以说分享与第三方登录已经成为了各大APP的必备功能。对于产品运行与推广来说,分享与第三方登录不仅能加强用户粘性,增加流量及新用户,也能提升用户存
CrazyCodeBoy
2018/05/07
1.9K0
React Native 集成分享第三方登录功能分享第三方登录模块开发(Android)
推荐阅读
相关推荐
React Native组件只Image
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档