【问题标题】:Unity IoC + WCF + wsHTTPBinding + Certificate AuthorizationUnity IoC + WCF + wsHTTPBinding + 证书授权
【发布时间】:2011-08-31 21:03:22
【问题描述】:

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

我在将 WCF web.config 配置与 Unity IoC 自定义服务主机和自定义行为混合时遇到问题。

当 Unity 完成所有配置后,它会创建简单的 BasicHttpBinding,但我的要求是通过证书授权使其安全,所以我需要 wsHTTPBinding。

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

首先看一下 WCF 的常见 Unity 实现:

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 应用程序 web.config 仅包含基本信息: 没有端点,没有服务定义。

现在假设我们有 SecurityService 的定义:

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

现在我可以将 SecurityService 的服务引用添加到我的客户端。 它在客户端 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" />

此时我为 Unity 配置了这个:

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

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

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

这一切都有效!但是如果我想使用 wsHTTPBinding 就不会...

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

为了启用 wsHTTPBinding,我在 WCF 应用程序的 web.config 中对其进行了配置。作为 BasicHttpBinding 的其余部分,它不包含任何有关绑定、端点等的信息。

但现在我添加了 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>

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

<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>

我手动添加了behaviorConfiguration="CertBehavior",即:

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

现在当我想使用 Unity 解决它时:

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

我总是为空...

当我通过以下方式简单地创建实例时有什么好笑的:

var client = new SecurityServiceReference.SecurityServiceClient();

它工作正常...所以可以肯定问题与错误的 wsHttpBinding 配置无关,而是结合了来自 web.config 的 Unity + wsHttpBinding...

谁能帮我解决这个问题?

丹尼尔

【问题讨论】:

  • 它看起来更像是您自己的代码中的问题。如果 Unity 无法创建实例,并且如果它尝试创建实例,它会调用构造函数,Unity 会触发异常 - 这样的调用不会导致 null

标签: asp.net-mvc wcf asp.net-mvc-3 unity-container wcf-security


【解决方案1】:

好的,我想通了。

Ladislav 你是对的,它应该显示一个异常。 UnityDependencyResolver 只是抓住了它。

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>();
        }
    }

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

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

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

现在可以正常使用了。

【讨论】:

    猜你喜欢
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多