WCF实际应用之IParameterInspector扩展

本文介绍了一种在WCF服务中实现自定义权限验证的方法,通过扩展IParameterInspector接口,并结合配置文件,实现了客户端调用服务接口时的身份验证及授权控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  最近开发一套由客户方定制的服务,据说之前版本是通过C写的WebService。那个神奇的Service我是没见过。只是有一点,之前的验证过程居然是这样进行的:客户端发送账号、密码,Service进行验证。验证成功后,Service会将产生一个加密字符,以类似Session方式存储并发送一个加密字符串给客户端。之后客户端每次调用服务都发送这个加密字符串以供服务端进行验证合法性。虽然个人觉得极为变态,但是交涉未果。另外还有一点需求就是:服务端会有一个授权文件,这个授权文件里面存放的是对客户端接口调用的授权信息。

  基于以上原因,考虑到WCF优秀的扩展性决定对他进行一定的扩展,以完成以上需求。

  由于客户端每次发送加密字符串用作身份凭证、以及授权的需求,因此选择IParameterInspector进行扩展。

      大家都知道通过配置的方式可以使程序更加灵活。那么我们应该考虑一下如下几个问题:

    1、如何通过配置的方式实现扩展?

    2、配置以后,怎样使我们的扩展对WCF框架生效呢。?

  对于第一个问题,在WCF中,可以通过继承BehaviorExtensionElement来实现;第二个问题,将参数检测应用到终结点行为上就能实现我们预期的目标,在这个过程中还需要实现IEndpointBehavior接口。

  开始介绍之前先看看IParameterInspector接口定义:

public interface IParameterInspector
 {
    /// <summary>
        /// 用于操作调用完成后
        /// </summary>
        /// <param name="operationName">接口操作名称</param>
        /// <param name="outputs">调用参数</param>
        /// <param name="returnValue">返回值</param>
        /// <param name="correlationState">与BeforeCall的关联状态</param>
    void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
        }

    
     /// <summary>
        /// 用于操作调用前
        /// </summary>
        /// <param name="operationName">接口操作名称</param>
        /// <param name="inputs">调用参数</param>
        /// <returns>AfterCall中的correlationState。如果AfterCall需要用到correlationState,就返回;否则返回null</returns>
        public object BeforeCall(string operationName, object[] inputs)
        {
    
        }
}

  实现扩展的步骤:

  1、实现IParameterInspector接口:

    public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
        }

        public object BeforeCall(string operationName, object[] inputs)
        {
            //登陆接口不做检查
            const string actionName = "Login";
            if (actionName == operationName)
            {
                return null;
            }
            Dictionary<string, Operation> dictionary = GetAuthorizationInfo();
            if (0 == dictionary.Count)
            {
                ServiceFaultException exception =
                    GetFormContainer(ConfigSetting.Instance.GetAuthorizationFileNotExistContainerName());
                throw new FaultException<ServiceFaultException>(exception, exception.Reason,
                                                                FaultCode.CreateSenderFaultCode(
                                                                    exception.ErrorCode.ToString(
                                                                        CultureInfo.InvariantCulture), string.Empty));
            }
            if (!dictionary.ContainsKey(operationName)
                || Operation.Deny == dictionary[operationName])
            {
                ServiceFaultException exception =
                    GetFormContainer(ConfigSetting.Instance.GetNotAccessContainerName());
                throw new FaultException<ServiceFaultException>(exception, exception.Reason,
                                                                FaultCode.CreateSenderFaultCode(
                                                                    exception.ErrorCode.ToString(
                                                                        CultureInfo.InvariantCulture), string.Empty));
            }
            bool flag = inputs.Any(input => (null != input) && CacheManager.ContainsKey(input.ToString()));
            if (!flag)
            {
                var exception = GetFormContainer(ConfigSetting.Instance.GetClientTicketOutContainerName());
                throw new FaultException<ServiceFaultException>(exception, exception.Details,
                                                                FaultCode.CreateSenderFaultCode(
                                                                    new FaultCode(
                                                                        exception.ErrorCode.ToString(
                                                                            CultureInfo.CurrentCulture))));
            }
            return null;
        }

  2、实现IEndpointBehavior接口,以将参数检测应用到DispatchRuntime的Operations的参数检查器中:

  internal class ValidEndpointBehavior : IEndpointBehavior
    {
        #region IEndpointBehavior Members

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            SynchronizedKeyedCollection<string, DispatchOperation> keyedCollection =
                endpointDispatcher.DispatchRuntime.Operations;
            foreach (var dispatchOperation in keyedCollection)
            {                
                dispatchOperation.ParameterInspectors.Add(new ParameterInspector());
            }

        }

        public void Validate(ServiceEndpoint endpoint)
        {
            
        }

        #endregion
    }

  注:以上扩展我只应用在了服务端。如果想应用在客户端,就应对ApplyClientBehavior中进行实现

  3、继承抽象类BehaviorExtesionElement,以在配置文件中配置参数检测器。

   internal class ExstensionBehaviorElement : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof (ValidEndpointBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new ValidEndpointBehavior();
        }
    }

  4、应用扩展:  

<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="endpointExstention"
             type="WcfExtensions.ExstensionBehaviorElement, WcfExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <bindings>
      <basicHttpBinding>
        <binding maxReceivedMessageSize="2147483647" name="vrvbasicBinding">
          <readerQuotas maxStringContentLength="2147483647"/>
        </binding>        
      </basicHttpBinding>
    </bindings>
    <serviceHostingEnvironment>
      <serviceActivations>
        <add service="VrvService.StateGrid.TerminalService" relativeAddress="TerminalService.svc"/>
        <add service="VrvService.StateGrid.MapRegionService" relativeAddress="MapRegionService.svc"/>
        <add service="VrvService.LoginService" relativeAddress="LoginService.svc"/>
      </serviceActivations>      
    </serviceHostingEnvironment>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="True"/>
          <dataContractSerializer maxItemsInObjectGraph="3000"/>          
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior>
          <endpointExstention/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>

  最后对IParameterInspector接口中AfterCall 中参数correlationState进行一下验证。我们应该如何应用它?

  我将IParameterInspector接口中BeforeCall改为如下:

        public object BeforeCall(string operationName, object[] inputs)
        {
            //登陆接口不做检查
            const string actionName = "Login";
            if (actionName == operationName)
            {
                return "ParameterInspector operationName is Login";
            }
        }

  然后客户端进行调用,跟踪AfterCall,如下图:

  由此可知如果想通过BeforeCall返回一些信息在AfterCall中进行处理,我们可以在BeforeCall实现中返回。

资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 “STC单片机电压测量”是一个以STC系列单片机为基础的电压检测应用案例,它涵盖了硬件电路设计、软件编程以及数据处理等核心知识点。STC单片机凭借其低功耗、高性价比和丰富的I/O接口,在电子工程领域得到了广泛应用。 STC是Specialized Technology Corporation的缩写,该公司的单片机基于8051内核,具备内部振荡器、高速运算能力、ISP(在系统编程)和IAP(在应用编程)功能,非常适合用于各种嵌入式控制系统。 在源代码方面,“浅雪”风格的代码通常简洁易懂,非常适合初学者学习。其中,“main.c”文件是程序的入口,包含了电压测量的核心逻辑;“STARTUP.A51”是启动代码,负责初始化单片机的硬件环境;“电压测量_uvopt.bak”和“电压测量_uvproj.bak”可能是Keil编译器的配置文件备份,用于设置编译选项和项目配置。 对于3S锂电池电压测量,3S锂电池由三节锂离子电池串联而成,标称电压为11.1V。测量时需要考虑电池的串联特性,通过分压电路将高电压转换为单片机可接受的范围,并实时监控,防止过充或过放,以确保电池的安全和寿命。 在电压测量电路设计中,“电压测量.lnp”文件可能包含电路布局信息,而“.hex”文件是编译后的机器码,用于烧录到单片机中。电路中通常会使用ADC(模拟数字转换器)将模拟电压信号转换为数字信号供单片机处理。 在软件编程方面,“StringData.h”文件可能包含程序中使用的字符串常量和数据结构定义。处理电压数据时,可能涉及浮点数运算,需要了解STC单片机对浮点数的支持情况,以及如何高效地存储和显示电压值。 用户界面方面,“电压测量.uvgui.kidd”可能是用户界面的配置文件,用于显示测量结果。在嵌入式系统中,用
资源下载链接为: https://pan.quark.cn/s/abbae039bf2a 在 Android 开发中,Fragment 是界面的一个模块化组件,可用于在 Activity 中灵活地添加、删除或替换。将 ListView 集成到 Fragment 中,能够实现数据的动态加载与列表形式展示,对于构建复杂且交互丰富的界面非常有帮助。本文将详细介绍如何在 Fragment 中使用 ListView。 首先,需要在 Fragment 的布局文件中添加 ListView 的 XML 定义。一个基本的 ListView 元素代码如下: 接着,创建适配器来填充 ListView 的数据。通常会使用 BaseAdapter 的子类,如 ArrayAdapter 或自定义适配器。例如,创建一个简单的 MyListAdapter,继承自 ArrayAdapter,并在构造函数中传入数据集: 在 Fragment 的 onCreateView 或 onActivityCreated 方法中,实例化 ListView 和适配器,并将适配器设置到 ListView 上: 为了提升用户体验,可以为 ListView 设置点击事件监听器: 性能优化也是关键。设置 ListView 的 android:cacheColorHint 属性可提升滚动流畅度。在 getView 方法中复用 convertView,可减少视图创建,提升性能。对于复杂需求,如异步加载数据,可使用 LoaderManager 和 CursorLoader,这能更好地管理数据加载,避免内存泄漏,支持数据变更时自动刷新。 总结来说,Fragment 中的 ListView 使用涉及布局设计、适配器创建与定制、数据绑定及事件监听。掌握这些步骤,可构建功能强大的应用。实际开发中,还需优化 ListView 性能,确保应用流畅运
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值