【问题标题】:How to enable Session with SSL wsHttpBinding in WCF如何在 WCF 中启用带有 SSL wsHttpBinding 的会话
【发布时间】:2012-09-16 16:51:34
【问题描述】:

我有一个启用了 wsHttpBindings 和 SSL 的 WCF 服务,但我想启用 WCF 会话。

将 SessionMode 更改为必需后

SessionMode:=SessionMode.Required

我收到如下所述的错误。

合同需要 Session,但绑定 'WSHttpBinding' 不支持 它或未正确配置以支持它。

这是我的示例应用程序。

App.config

  <?xml version="1.0" encoding="utf-8" ?>
    <configuration>

      <system.web>
        <compilation debug="true" />
      </system.web>
      <!-- When deploying the service library project, the content of the config file must be added to the host's 
      app.config file. System.Configuration does not support config files for libraries. -->
      <system.serviceModel>

        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
        <client />
        <bindings>
          <wsHttpBinding>
            <binding name="NewBinding0" useDefaultWebProxy="false" allowCookies="true">
              <readerQuotas maxStringContentLength="10240" />
              <!--reliableSession enabled="true" /-->
              <security mode="Transport">
                <transport clientCredentialType="None" proxyCredentialType="None" >
                  <extendedProtectionPolicy policyEnforcement="Never" />
                </transport >
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        <services>
          <service name="WcfServiceLib.TestService">
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="NewBinding0"
              contract="WcfServiceLib.ITestService">
              <identity>
                <servicePrincipalName value="Local Network" />
              </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
            <host>
              <baseAddresses>
                <add baseAddress="https://test/TestService.svc" />
              </baseAddresses>
            </host>
          </service>
        </services>

        <behaviors>
          <serviceBehaviors>
            <behavior>
              <!-- To avoid disclosing metadata information, 
              set the value below to false and remove the metadata endpoint above before deployment -->
              <serviceMetadata httpsGetEnabled="True"/>
              <!-- To receive exception details in faults for debugging purposes, 
              set the value below to true.  Set to false before deployment 
              to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="False" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>

    </configuration>

ITestService.vb

  <ServiceContract(SessionMode:=SessionMode.Required)>
    Public Interface ITestService

        <OperationContract(IsInitiating:=True, IsTerminating:=False)> _
        Function GetData(ByVal value As Integer) As String

    End Interface

TestService.vb

    <ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession, _ 
    ReleaseServiceInstanceOnTransactionComplete:=False, _ 
    ConcurrencyMode:=ConcurrencyMode.Single)>
        Public Class TestService
            Implements ITestService

            Private _user As User

            <OperationBehavior(TransactionScopeRequired:=True)>
            Public Function GetData(ByVal value As Integer) As String _
 Implements ITestService.GetData

                If _user Is Nothing Then

                    _user = New User()
                    _user.userName = "User_" & value
                    _user.userPassword = "Pass_" & value

                    Return String.Format("You've entered: {0} , Username = {1} , Password = {2} ", _
                                         value, _user.userName, _user.userPassword)
                Else
                    Return String.Format("Username = {1} , Password = {2} ", _
                                    _user.userName, _user.userPassword)
                End If

            End Function

        End Class

我尝试了所有可能的解决方案,我可以找到,但没有任何帮助。

一些关于启用可靠会话的建议,但它不适用于 ssl(如果您有自定义绑定),其他建议使用 http 而不是 https,但如果可能的话,我想使用我当前的配置启用会话。

有什么方法可以做到这一点吗?

非常感谢任何形式的帮助。

【问题讨论】:

  • 在 wcf 服务上启用会话状态的原因是什么?如果您正在寻找高性能,这可能是一个瓶颈。应避免使用会话状态 wcf 服务。
  • 我想为每个会话设置一个 _user 变量。每次调用我都会得到一个新的服务类实例。 _user 的值始终为 Nothing。
  • 如果我需要为每个客户存储数据,我应该针对我的情况使用什么解决方案?
  • 您可以将用户变量存储在 wcf 请求自定义标头中并在服务器端读取它。或者您可以从请求中读取用户身份。这将为您完成这项工作。
  • 我想将变量存储在服务器端,而不是将它们传递给客户端并返回。

标签: .net wcf session ssl https


【解决方案1】:

如果您想要使用 wsHttpBinding 的“会话”,您必须使用可靠消息传递或安全会话。 (来源:how to enable WCF Session with wsHttpBidning with Transport only Security)。

WSHttpBinding 支持会话,但前提是启用了安全性 (SecureConversation) 或可靠消息传递。 如果您使用传输安全性,那么它不使用 WS-SecureConversation 并且默认情况下 WS-ReliableMessaging 处于关闭状态。因此 WSHttpBinding 用于会话的两种协议不可用。您要么需要使用消息安全性,要么需要打开可靠会话。 (来源:http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/57b3453e-e7e8-4875-ba23-3be4fff080ea/)。

我们在标准绑定中不允许 RM over Https,因为保护 RM 会话的方法是使用安全会话,而 Https 不提供会话。

我在这里找到了关于它的 msdn 简介:http://msdn2.microsoft.com/en-us/library/ms733136.aspx 简介是“唯一的例外是使用 HTTPS 时。 SSL 会话未绑定到可靠会话。这造成了威胁,因为共享安全上下文(SSL 会话)的会话彼此之间没有受到保护;这可能是也可能不是真正的威胁,具体取决于应用程序。”

但是,如果您确定没有威胁,您可以这样做。通过自定义绑定http://msdn2.microsoft.com/en-us/library/ms735116.aspx(来源:http://social.msdn.microsoft.com/forums/en-US/wcf/thread/fb4e5e31-e9b0-4c24-856d-1c464bd0039c/)有一个 RM over HTTPS 示例。

总结您的可能性,您可以:

1 - 保留 wsHttpBinding,移除传输安全性并启用可靠消息传递

  <wsHttpBinding>
    <binding name="bindingConfig">
      <reliableSession enabled="true" />
      <security mode="None"/>
    </binding>
  </wsHttpBinding>

但是你失去了 SSL 层,然后是你的一部分安全性。

2 - 保持 wsHttpBinding,保持传输安全并添加消息认证

  <wsHttpBinding>
    <binding name="bindingConfig">
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="UserName"/>
      </security>
    </binding>
  </wsHttpBinding>

您可以保留 SSL 安全层,但您的客户端必须提供(任何形式的)凭据,即使您不在服务端验证它们,仍然必须提供假的,因为 WCF 会拒绝任何消息没有指定凭据。

3 - 使用具有可靠消息传递和 HTTPS 传输的自定义绑定

  <customBinding>
    <binding name="bindingConfig">
      <reliableSession/>
      <httpsTransport/>
    </binding>
  </customBinding>

除了 MSDN 中解释的威胁外,我看不出有任何不利之处,这取决于您的应用程序。

4 - 使用其他会话提供程序 如果你的应用程序在 IIS 中是 hotsed,你可以设置

<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

并依赖于

HttpContext.Current.Session

为您的州。

或实施您自己的 cookie。

PS:请注意,对于所有这些 WCF 配置,我只测试了服务激活,而不是调用。

编辑:根据用户请求,wsHttpBindingTransportWithMessageCredential 安全模式实现会话(我对 VB.NET 不太熟悉,所以请原谅我的语法):

服务代码sn-p:

<ServiceContract(SessionMode:=SessionMode.Required)>
Public Interface IService1

    <OperationContract()> _
    Sub SetSessionValue(ByVal value As Integer)

    <OperationContract()> _
    Function GetSessionValue() As Nullable(Of Integer)

End Interface

<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession, 
    ConcurrencyMode:=ConcurrencyMode.Single)>
Public Class Service1
    Implements IService1

    Private _sessionValue As Nullable(Of Integer)

    Public Sub SetSessionValue(ByVal value As Integer) Implements IService1.SetSessionValue
        _sessionValue = value
    End Sub

    Public Function GetSessionValue() As Nullable(Of Integer) Implements IService1.GetSessionValue
        Return _sessionValue
    End Function
End Class

Public Class MyUserNamePasswordValidator
    Inherits System.IdentityModel.Selectors.UserNamePasswordValidator

    Public Overrides Sub Validate(userName As String, password As String)
        ' Credential validation logic
        Return ' Accept anything
    End Sub

End Class

服务配置sn-p:

<system.serviceModel>
  <services>
    <service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior">
      <endpoint address="" binding="wsHttpBinding" contract="WcfService1.IService1" bindingConfiguration="bindingConf"/>
      <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
    </service>
  </services>
  <bindings>
    <wsHttpBinding>
      <binding name="bindingConf">
        <security mode="TransportWithMessageCredential">
          <message clientCredentialType="UserName"/>
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
  <behaviors>
    <serviceBehaviors>
      <behavior name="WcfService1.Service1Behavior">
        <serviceMetadata httpsGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
        <serviceCredentials>
          <userNameAuthentication 
            userNamePasswordValidationMode="Custom"
            customUserNamePasswordValidatorType="WcfService1.MyUserNamePasswordValidator, WcfService1"/>
        </serviceCredentials>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

客户端测试代码sn -p:

Imports System.Threading.Tasks

Module Module1

    Sub Main()
        Parallel.For(0, 10, Sub(i) Test(i))
        Console.ReadLine()
    End Sub

    Sub Test(ByVal i As Integer)
        Dim client As ServiceReference1.Service1Client
        client = New ServiceReference1.Service1Client()
        client.ClientCredentials.UserName.UserName = "login"
        client.ClientCredentials.UserName.Password = "password"
        Console.WriteLine("Session N° {0} : Value set to {0}", i)
        client.SetSessionValue(i)
        Dim response As Nullable(Of Integer)
        response = client.GetSessionValue()
        Console.WriteLine("Session N° {0} : Value returned : {0}", response)
        client.Close()
    End Sub

End Module

【讨论】:

  • 我没有时间测试你的解决方案,赏金在10分钟后结束,但这个答案是最详细的,所以我会接受它。适合我使用自定义绑定或 TransportWithMessageCredential 模式。我会尝试他们两个。谢谢。
  • TransportWithMessageCredential 不适用于 wsHttpBinding。它仅适用于 NetHttpBinding。检查msdn.microsoft.com/en-us/library/ms730879.aspx
  • 这意味着对我来说唯一可能的方法是使用自定义绑定。
  • @hgulyan 为什么TransportWithMessageCredential 不能与wsHttpBinding 一起工作?这是一个相当常见的场景。从您的链接:WSHttpBinding:安全:传输,(消息),混合;会话:(无)、可靠会话、安全会话
  • @hgulyan 我认为这只是默认设置,我试过了,它工作得很好,启用了会话。我可以编辑答案以显示完整的代码示例。
【解决方案2】:

默认情况下,WSHttpBinding 只允许安全会话。它是 WCF 的一个概念,与 Transport 无关。安全会话不是基于 https 的会话,而是具有相互身份验证的会话。这是通过增加消息安全性来实现的。

根据您的服务,您应该应用此配置

 <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBindingConfig">
          <security mode="TransportWithMessageCredential">
            <!-- configure transport & message security here if needed-->
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

在客户端,这是一个简单的单元测试

[TestMethod]
    public void TestSecuritySession()
    {
        //remove this is certificate is valid
        ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) => { return true; });

        var binding = new WSHttpBinding();
        binding.Security = new WSHttpSecurity() { Mode = SecurityMode.TransportWithMessageCredential};
        ChannelFactory<ITestService> Factory = new ChannelFactory<ITestService>(binding, new EndpointAddress("https://localhost/TestService.svc"));

        Factory.Open();
        var channel = Factory.CreateChannel();

        //call service here

        (channel as IClientChannel).Close();

        Factory.Close();
    }

【讨论】:

  • 如果我将 InstanceContextMode 设为 PerSession,我的会话是否适用于您的情况?
  • @hgulyan 当然是的。您可以通过在一种服务方法中返回特定于会话/实例的数据来轻松地测试此行为。
【解决方案3】:

wsHttpBinding 需要reliableSession 来支持WCF 会话,而可靠会话需要自定义绑定来支持ssl。因此,据我所知,通过 ssl 和 wsHttpBinding 请求 WCF 会话似乎是不可能的。

【讨论】:

  • 我想,你是对的,但如果我需要 ssl 和 session 工作,我该怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多