WCF进阶:扩展bindingElementExtensions支持对称加密传输

    前面两篇文章WCF进阶:将编码后的字节流压缩传输WCF 进阶: 对称加密传输都是实现了自定义编码,那两个例子中托管服务或者客户端调用都采用的代码实现,WCF更友好的方式是在app.config或web.config来配置服务的运行和调用,本文是介绍如何在配置文件中配置自定义的BindingElement。

    上文WCF 进阶: 对称加密传输中我们实现了一个自定义的BindingElement:CryptEncodingBindingElement,它是一个MessageEncodingBindingElement,在宿主程序中,我们通过代码的方式将其添加到CustomBinding中去,方法为:

ICollection<BindingElement> bindingElements = new List<BindingElement>();
HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
string key = "JggkieIw7JM=";
string iv = "XdTkT85fZ0U=";
CryptEncodingBindingElement textBindingElement = new CryptEncodingBindingElement(new BinaryMessageEncodingBindingElement(), key, iv);
bindingElements.Add(textBindingElement);
bindingElements.Add(httpBindingElement); 
CustomBinding bind = new CustomBinding(bindingElements);  

如果是缺省的BindingElement,如TextMessageEncodingElement,HttpTransportElement,ReliableSessionElement,SecurityElement都可以像如下这样的方式进行配置:

<bindings>
  <customBinding>
    <binding name="myBinding">
      <textMessageEncoding/>
      <reliableSession/>
      <security/>
      <httpTransport/>
    </binding>
  </customBinding>
</bindings>

如果要想让我们自定义的BindingElement和上面的一样享受同等待遇的话,我们需要再额外做一些事情,那就是重载BindingElementExtensionElement,在.Net中扩展配置的基类为:ConfigurationElement,它定义声明了一些扩展配置的属性和方法,为了方便WCF重载了ConfigurationElement形成了ServiceModelExtensionElement,而为了进一步的方便扩展BindingElement,又提供了BindingElementExtensionElement的基类,创建自定义的BindingElementExtension,只需要重载BindingElementExtensionElement三个方法:

using System;
using System.ServiceModel.Channels;

namespace System.ServiceModel.Configuration
{
    // 摘要:
    //     为使用计算机或应用程序配置文件中的自定义 System.ServiceModel.Channels.BindingElement 实现提供支持。
    public abstract class BindingElementExtensionElement : ServiceModelExtensionElement
    {
        // 摘要:
        //     初始化 System.ServiceModel.Configuration.BindingElementExtensionElement 类的新实例。
        protected BindingElementExtensionElement();

        // 摘要:
        //     在派生类中重写时,获取表示自定义绑定元素的 System.Type 对象。
        //
        // 返回结果:
        //     一个表示自定义绑定类型的 System.Type 对象。
        public abstract Type BindingElementType { get; }

        // 摘要:
        //     将指定绑定元素的内容应用到此绑定配置元素。
        //
        // 参数:
        //   bindingElement:
        //     一个绑定元素。
        //
        // 异常:
        //   System.ArgumentNullException:
        //     bindingElement 为 null。
        public virtual void ApplyConfiguration(BindingElement bindingElement);
        //
        // 摘要:
        //     在派生类中重写时,返回一个自定义绑定元素对象。
        //
        // 返回结果:
        //     一个自定义 System.ServiceModel.Channels.BindingElement 对象。
        protected internal abstract BindingElement CreateBindingElement();
        //
        // 摘要:
        //     使用指定绑定元素的内容来初始化此绑定配置节。
        //
        // 参数:
        //   bindingElement:
        //     一个绑定元素。
        protected internal virtual void InitializeFrom(BindingElement bindingElement);
    }
}

public abstract Type BindingElementType { get; } 这个属性中只需要返回自定义类的类型,protected internal abstract BindingElement CreateBindingElement()的重载就是在这创建需要的自定义BindingElement,如果自定义的BindingElement有额外的属性或者参数,我们可以还可以通过创建带有ConfigurationPropertyAttribute的属性来指定。这个是.Net配置中的通性。在我们的需求中,需要一个CryptEncodingBindingElement实例,而CryptEncodingBindingElement带有三个参数:InnerMessageEncodingBindingElement,Key,IV。分析到这,我们的自定义BindingElement配置扩展类就成形了,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Configuration;
using System.Configuration;

namespace RobinLib
{
    public class CryptEncodingBindingElementConfiguration : BindingElementExtensionElement
    {
        public override void ApplyConfiguration(System.ServiceModel.Channels.BindingElement bindingElement)
        {
            CryptEncodingBindingElement bind = bindingElement as CryptEncodingBindingElement;
            if (InnerMessageEncoding.ToLower() == "text")
            {
                bind.InnerMessageEncodingBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
            }
            else if (InnerMessageEncoding.ToLower() == "binary")
            { 
                bind.InnerMessageEncodingBindingElement = new System.ServiceModel.Channels.BinaryMessageEncodingBindingElement();
            }
            else if (InnerMessageEncoding.ToLower() == "mtom")
            {
                bind.InnerMessageEncodingBindingElement = new System.ServiceModel.Channels.MtomMessageEncodingBindingElement();
                
            }
            bind.Key = Key;
            bind.IV = IV;
            base.ApplyConfiguration(bindingElement);
        }


        [ConfigurationProperty("innerMessageEncoding",DefaultValue="text")]
        public string InnerMessageEncoding
        {
            get
            {
                return base["innerMessageEncoding"] as string;
            }
            set
            {
                base["innerMessageEncoding"] = value;
            }
        }


        [ConfigurationProperty("key", DefaultValue = "")]
        public string Key
        {
            get
            {
                return base["key"] as string;
            }
            set
            {
                base["key"] = value;
            }
        }

        [ConfigurationProperty("iv", DefaultValue = "")]
        public string IV
        {
            get
            {
                return base["iv"] as string;
            }
            set
            {
                base["iv"] = value;
            }
        }

        public override Type BindingElementType
        {
            get { return typeof(CryptEncodingBindingElementConfiguration); }
        }

        protected override System.ServiceModel.Channels.BindingElement CreateBindingElement()
        {
            if (InnerMessageEncoding.ToLower() == "text")
            {
                CryptEncodingBindingElement bindElement = new CryptEncodingBindingElement(new System.ServiceModel.Channels.TextMessageEncodingBindingElement(), Key, IV);
                return bindElement;
            }
            else if (InnerMessageEncoding.ToLower() == "binary")
            {
                CryptEncodingBindingElement bindElement = new CryptEncodingBindingElement(new System.ServiceModel.Channels.BinaryMessageEncodingBindingElement(), Key, IV);
                return bindElement;
            }
            else if (InnerMessageEncoding.ToLower() == "mtom")
            {
                CryptEncodingBindingElement bindElement = new CryptEncodingBindingElement(new System.ServiceModel.Channels.MtomMessageEncodingBindingElement(), Key, IV);
                return bindElement;
            }
            throw new Exception("只支持Text,Binary,MTOM三种内置编码器!");
        }
    }
}

重载实现之后,我们就可以使用它在App.Config或者Web.Config来配置BindingElement,使用的方法为:

1. 在<system.serviceModel><extensions> <bindingElementExtensions>中添加新的配置节点的绑定。

2. 创建新的注册后的节点到自定义绑定中。

来看一下服务的配置文件吧:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <clear/>
      <service name="Robin_Wcf_CustomMessageEncoder_SvcLib.Service1">
        <host>
          <baseAddresses>
            <add baseAddress="http://127.0.0.1:8081/"/>
          </baseAddresses>
        </host>
        <endpoint address="Robin_Wcf_Formatter" name="ep" contract="Robin_Wcf_CustomMessageEncoder_SvcLib.IService1"  binding="customBinding" bindingConfiguration="myBinding"></endpoint>
      </service>
    </services>
    <bindings>
      <customBinding>
        <binding name="myBinding">
          <cryptMessageEncoding key="JggkieIw7JM=" iv="XdTkT85fZ0U=" innerMessageEncoding="Binary"/>
          <httpTransport/>
        </binding>
      </customBinding>
    </bindings>
    <extensions> 
      <bindingElementExtensions>
        <add name="cryptMessageEncoding" type="RobinLib.CryptEncodingBindingElementConfiguration,RobinLib,Version=1.0.0.0, Culture=neutral,PublicKeyToken=null"/>
      </bindingElementExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

托管服务的代码就轻便了好多,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Robin_Wcf_CustomMessageEncoder_SvcLib;
using System.ServiceModel.Channels;
using RobinLib;

namespace Robin_Wcf_CustomMessageEncoder_Host
{
    class Program
    {
        static void Main(string[] args)
        { 
            ServiceHost host = new ServiceHost(typeof(Service1)); 
            if (host.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>() == null)
            {
                System.ServiceModel.Description.ServiceMetadataBehavior svcMetaBehavior = new System.ServiceModel.Description.ServiceMetadataBehavior();
                svcMetaBehavior.HttpGetEnabled = true;
                svcMetaBehavior.HttpGetUrl = new Uri("http://127.0.0.1:8001/Mex");
                host.Description.Behaviors.Add(svcMetaBehavior);
            }
            host.Opened += new EventHandler(delegate(object obj, EventArgs e)
            {
                Console.WriteLine("服务已经启动!");
            }); 
            host.Open();
            Console.Read();
        }
    }
}

运行项目,能得到和WCF 进阶: 对称加密传输中一致的输出!

posted @ 2010-04-17 23:45 Robin Zhang 阅读(...) 评论(...) 编辑 收藏