Unity IoC + WCF + wsHTTPBinding +证书授权

时间:2022-10-16 04:09:44

I have two applications on IIS for development. First WCF application that contains all the logic and communication with database (we call this Server). And another ASP.NET MVC 3 application that has reference to WCF application (we call this Client).

我在IIS上有两个应用程序用于开发。第一个WCF应用程序,它包含所有与数据库的逻辑和通信(我们称之为服务器)。和另一个ASP。NET MVC 3应用程序,它引用了WCF应用程序(我们称之为客户端)。

I have issue connected with mixing WCF web.config configuration with Unity IoC custom service host and custom behavior.

我有一个问题与WCF网络的混合。配置配置与统一IoC自定义服务主机和自定义行为。

When all configuration is done by Unity it creates simple BasicHttpBinding, but my requirement is to make it secure with Certificate Authorization, so I need wsHTTPBinding.

当所有配置都由Unity完成时,它会创建简单的BasicHttpBinding,但是我的需求是使用证书授权确保它的安全性,所以我需要wsHTTPBinding。

------------- Configuring for BasicHttpBinding ------------

配置为BasicHttpBinding - - - - - - - - - - - - - - - - - - - - - - - - -

At the beginning look at common Unity implementation for WCF:

首先,我们来看一下WCF的统一实现:

internal class UnityInstanceProvider :  IInstanceProvider
{
    private readonly IUnityContainer container;
    private readonly Type contractType;

    public UnityInstanceProvider(
        [NotNull] IUnityContainer container, 
        [NotNull] Type contractType)
    {
        this.container = container;
        this.contractType = contractType;
    }

    #region IInstanceProvider Members

    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return container.Resolve(contractType);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        container.Teardown(instance);
    }
}

internal class UnityServiceBehavior : IServiceBehavior
{
    private readonly IUnityContainer container;

    public UnityServiceBehavior(
        [NotNull] IUnityContainer container)
    {
        this.container = container;
    }

    #region IServiceBehavior Members

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {

    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
            {
                if (endpointDispatcher.ContractName != "IMetadataExchange")
                {
                    endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(container, serviceDescription.ServiceType);



                }
            }
        }
    }

    #endregion
}

public class UnityServiceHost : ServiceHost
{
    private readonly IUnityContainer container;

    public UnityServiceHost(
        [NotNull] IUnityContainer container, 
        [NotNull] Type serviceType, 
        Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
        this.container = container;
    }

    protected override void OnOpening()
    {
        base.OnOpening();
        if (Description.Behaviors.Find<UnityServiceBehavior>() == null)
        {
            Description.Behaviors.Add(new UnityServiceBehavior(container));
        }
    }
}

public class UnityServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        IUnityContainer container = new UnityContainer();
        UnityContainerConfigurator.Configure(container);
        return new UnityServiceHost(container, serviceType, baseAddresses);
    }
}

WCF application web.config contains only basic information: None endpoints, none service definition.

WCF应用程序web。配置只包含基本信息:没有端点,没有服务定义。

Now imagine that we have SecurityService with definition:

现在假设我们有定义为

<%@ ServiceHost Language="C#" Debug="true" 
Service="myNamespace.SecurityService" 
Factory="myNamespace.UnityServiceHostFactory" %>

Now I can add service reference to SecurityService to my Client. A this step it generates in client web.config:

现在我可以将服务引用添加到我的客户端SecurityService中。A在客户端web.config中生成这个步骤:

<basicHttpBinding>
<binding name="BasicHttpBinding_ISecurityService" closeTimeout="00:01:00"
 openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
 allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
 maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
 messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
 useDefaultWebProxy="true">
 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
  maxBytesPerRead="4096" maxNameTableCharCount="16384" />
 <security mode="None">
  <transport clientCredentialType="None" proxyCredentialType="None"
   realm="" />
  <message clientCredentialType="UserName" algorithmSuite="Default" />
 </security>
</binding>

<endpoint address="http://localhost/wcf-app/SecurityService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISecurityService"
contract="SecurityServiceReference.ISecurityService" name="BasicHttpBinding_ISecurityService" />

At this point I configure this for Unity:

此时,我将此配置为Unity:

container.RegisterType<SecurityServiceClient>(new InjectionConstructor());

And in Client application I can use it simply by (I don't mention here constructor injection):

在客户机应用程序中,我可以简单地使用它(我在这里没有提到构造函数注入):

var securityService = DependencyResolver.Current.GetService<SecurityServiceClient>();

And this all works! But It don't if I want to use wsHTTPBinding ...

这所有的工作!但如果我想用wsHTTPBinding…

------------- Configuring for wsHTTPBinding ------------

配置为wsHTTPBinding - - - - - - - - - - - - - - - - - - - - - - - - -

To enable wsHTTPBinding I configured it inside web.config of WCF Application. As a remainder for BasicHttpBinding it didn't contained any information concerning binding, endpoin, etc.

为了启用wsHTTPBinding,我在web中配置了它。WCF应用程序的配置。作为BasicHttpBinding的其余部分,它没有包含任何关于binding、endpoin等的信息。

But now for wsHTTPBinding I added:

但现在我补充说,wsHTTPBinding:

<bindings>
  <wsHttpBinding>
    <binding name="wsHttpEndpointBinding">
      <security>
        <message clientCredentialType="Certificate" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

<services>   
  <service behaviorConfiguration="ServiceBehavior" name="myNamespace.SecurityService">
    <endpoint address="" binding="wsHttpBinding"
      bindingConfiguration="wsHttpEndpointBinding"
      name="wsHttpEndpoint" contract="myNamespace.ISecurityService">
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>

<behaviors>
  <serviceBehaviors>
    <behavior name="ServiceBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
      <serviceCredentials>
        <serviceCertificate findValue="CN=myClientCert" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

And after adding service reference to Client application it generates:

在将服务引用添加到客户机应用程序后,它生成:

<wsHttpBinding>
<binding name="wsHttpEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00"
 receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
 transactionFlow="false" hostNameComparisonMode="StrongWildcard"
 maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
 textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
  maxBytesPerRead="4096" maxNameTableCharCount="16384" />
 <reliableSession ordered="true" inactivityTimeout="00:10:00"
  enabled="false" />
 <security mode="Message">
  <transport clientCredentialType="Windows" proxyCredentialType="None"
   realm="" />
  <message clientCredentialType="Certificate" negotiateServiceCredential="true"
   algorithmSuite="Default" />
 </security>
</binding>

I manually added behaviorConfiguration="CertBehavior", that is:

我手动添加行为配置="CertBehavior",即:

<behaviors>
 <endpointBehaviors>
   <behavior name="CertBehavior">
     <clientCredentials>
       <clientCertificate findValue="CN=myClientCert"/>
     </clientCredentials>
   </behavior>
 </endpointBehaviors>

And now when I want to resolve it using Unity:

现在,当我想用Unity来解决它的时候

var securityService = DependencyResolver.Current.GetService<SecurityServiceClient>();

I get always null...

我总是空…

What is funny when I create simply instance by:

当我创建简单的实例:

var client = new SecurityServiceReference.SecurityServiceClient();

It works fine... So for sure issue is not connected with wrong wsHttpBinding configuration but rather combining Unity + wsHttpBinding from web.config...

它工作正常…因此,可以肯定的是,问题并没有与错误的wsHttpBinding配置相关联,而是与Unity + wsHttpBinding结合,从web.config…

Can any one help me with this issue?

有谁能帮我解决这个问题吗?

Daniel

丹尼尔

1 个解决方案

#1


1  

Ok, I figured it out.

好的,我算出来了。

Ladislav you were right that it should show an exception. UnityDependencyResolver was simply catching it.

Ladislav你是对的,应该有例外。独立的,独立的。

internal class UnityDependencyResolver : IDependencyResolver
{
    private readonly IUnityContainer container;

    public UnityDependencyResolver([NotNull] IUnityContainer container)
    {
        this.container = container;
    }

    #region IDependencyResolver Members

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch
        {
            return new List<object>();
        }
    }

I also had to explicitly set certificate locations for the certificates:

我还必须为证书明确设置证书的位置:

<serviceCertificate findValue="CN=myClientCert"
                            storeName="My"
                            x509FindType="FindBySubjectDistinguishedName"
                            storeLocation="LocalMachine"
                            />

<clientCertificate findValue="CN=myClientCert" storeName="My"
                            x509FindType="FindBySubjectDistinguishedName"
                            storeLocation="LocalMachine"
                            />

Now it works fine.

现在工作正常。

#1


1  

Ok, I figured it out.

好的,我算出来了。

Ladislav you were right that it should show an exception. UnityDependencyResolver was simply catching it.

Ladislav你是对的,应该有例外。独立的,独立的。

internal class UnityDependencyResolver : IDependencyResolver
{
    private readonly IUnityContainer container;

    public UnityDependencyResolver([NotNull] IUnityContainer container)
    {
        this.container = container;
    }

    #region IDependencyResolver Members

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch
        {
            return new List<object>();
        }
    }

I also had to explicitly set certificate locations for the certificates:

我还必须为证书明确设置证书的位置:

<serviceCertificate findValue="CN=myClientCert"
                            storeName="My"
                            x509FindType="FindBySubjectDistinguishedName"
                            storeLocation="LocalMachine"
                            />

<clientCertificate findValue="CN=myClientCert" storeName="My"
                            x509FindType="FindBySubjectDistinguishedName"
                            storeLocation="LocalMachine"
                            />

Now it works fine.

现在工作正常。