如何在 WCF 中使用 SSL wsHttpBinding 启用会话 [英] How to enable Session with SSL wsHttpBinding in WCF

查看:28
本文介绍了如何在 WCF 中使用 SSL wsHttpBinding 启用会话的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

将 SessionMode 改为 required 后

SessionMode:=SessionMode.Required

我收到如下所述的错误.

<块引用>

Contract 需要 Session,但 Binding 'WSHttpBinding' 不支持它或未正确配置以支持它.

这是我的示例应用程序.

App.config

 <?xml version="1.0" encoding="utf-8" ?><配置><system.web><编译调试=真"/></system.web><!-- 部署服务库项目时,必须将config文件的内容添加到宿主机的app.config 文件.System.Configuration 不支持库的配置文件.--><system.serviceModel><serviceHostingEnvironment aspNetCompatibilityEnabled="true"/><客户/><绑定><wsHttpBinding><binding name="NewBinding0" useDefaultWebProxy="false" allowCookies="true"><readerQuotas maxStringContentLength="10240"/><!--reliableSession enabled="true"/--><安全模式="传输"><传输 clientCredentialType="None" proxyCredentialType="None" ><extendedProtectionPolicy policyEnforcement="Never"/></运输 ></安全></binding></wsHttpBinding></绑定><服务><服务名称="WcfServiceLib.TestService"><端点地址="" binding="wsHttpBinding" bindingConfiguration="NewBinding0"合同="WcfServiceLib.ITestService"><身份><servicePrincipalName value="本地网络"/></身份></端点><endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/><主机><基地址><add baseAddress="https://test/TestService.svc"/></baseAddresses></host></服务></服务><行为><服务行为><行为><!-- 为避免泄露元数据信息,将下面的值设置为 false 并在部署之前删除上面的元数据端点 --><serviceMetadata httpsGetEnabled="True"/><!-- 接收故障中的异常详细信息以进行调试,将下面的值设置为 true.部署前设置为false避免泄露异常信息--><serviceDebug includeExceptionDetailInFaults="False"/></行为></serviceBehaviors></行为></system.serviceModel></配置>

ITestService.vb

 公共接口 ITestService<OperationContract(IsInitiating:=True, IsTerminating:=False)>_函数 GetData(ByVal value As Integer) As String终端接口

TestService.vb

 公共类测试服务实现 ITestService私人 _user 作为用户<OperationBehavior(TransactionScopeRequired:=True)>公共函数 GetData(ByVal value As Integer) As String _实现 ITestService.GetData如果 _user 什么都没有,那么_user = 新用户()_user.userName = "用户_" &价值_user.userPassword = "Pass_" &价值Return String.Format("您已输入:{0} , 用户名 = {1} , 密码 = {2} ", _值、_user.userName、_user.userPassword)别的Return String.Format("用户名 = {1} , 密码 = {2} ", __user.userName, _user.userPassword)万一结束函数结束班

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

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

有什么方法可以实现这一目标吗?

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

解决方案

如果您希望使用 wsHttpBinding 进行会话",则必须使用可靠消息传递或安全会话.(来源:如何使用 wsHttpBidning 启用 WCF 会话仅传输安全).

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

我们在标准绑定中禁止通过 Https 使用 RM,因为保护 RM 会话的方法是使用安全会话,而 Https 不提供会话.

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

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

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

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

 <绑定名称=bindingConfig"><reliableSession enabled="true"/><安全模式=无"/></binding></wsHttpBinding>

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

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

 <绑定名称=bindingConfig"><安全模式="TransportWithMessageCredential"><message clientCredentialType="用户名"/></安全></binding></wsHttpBinding>

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

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

 <绑定名称=bindingConfig"><reliableSession/><https传输/></binding></customBinding>

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

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

并依赖于

HttpContext.Current.Session

为了你的州.

或者实现您自己的 cookie.

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

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

服务代码片段:

公共接口 IService1<OperationContract()>_Sub SetSessionValue(ByVal value As Integer)<OperationContract()>_函数 GetSessionValue() As Nullable(Of Integer)终端接口<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession,ConcurrencyMode:=ConcurrencyMode.Single)>公开课服务1实现 IService1Private _sessionValue 为 Nullable(Of Integer)Public Sub SetSessionValue(ByVal value As Integer) 实现 IService1.SetSessionValue_sessionValue = 值结束子公共函数 GetSessionValue() As Nullable(Of Integer) 实现 IService1.GetSessionValue返回 _sessionValue结束函数结束班公共类 MyUserNamePasswordValidator继承 System.IdentityModel.Selectors.UserNamePasswordValidatorPublic Overrides Sub Validate(userName As String, password As String)' 凭证验证逻辑Return '接受任何东西结束子结束班

服务配置片段:

<服务><服务名称="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior"><endpoint address="" binding="wsHttpBinding" contract="WcfService1.IService1" bindingConfiguration="bindingConf"/><endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/></服务></服务><绑定><wsHttpBinding><binding name="bindingConf"><安全模式="TransportWithMessageCredential"><message clientCredentialType="用户名"/></安全></binding></wsHttpBinding></绑定><行为><服务行为><行为名称="WcfService1.Service1Behavior"><serviceMetadata httpsGetEnabled="true"/><serviceDebug includeExceptionDetailInFaults="false"/><serviceCredentials><用户名认证userNamePasswordValidationMode="自定义"customUserNamePasswordValidatorType="WcfService1.MyUserNamePasswordValidator, WcfService1"/></serviceCredentials></行为></serviceBehaviors></行为></system.serviceModel>

客户端测试代码片段:

导入 System.Threading.Tasks模块模块1子主()Parallel.For(0, 10, Sub(i) Test(i))Console.ReadLine()结束子子测试(ByVal i As Integer)Dim client As ServiceReference1.Service1Client客户端 = 新的 ServiceReference1.Service1Client()client.ClientCredentials.UserName.UserName = "登录"client.ClientCredentials.UserName.Password = "密码"Console.WriteLine("Session N° {0} : 值设置为 {0}", i)client.SetSessionValue(i)Dim 响应为 Nullable(Of Integer)响应 = client.GetSessionValue()Console.WriteLine("Session N° {0} : 返回值:{0}", response)客户端.关闭()结束子终端模块

I have a WCF Service with wsHttpBindings and SSL enabled, but I'd like to enable WCF sessions.

After changing SessionMode to required

SessionMode:=SessionMode.Required

 I'm getting error described below. 

Contract requires Session, but Binding 'WSHttpBinding' doesn't support it or isn't configured properly to support it.

Here's my sample application.

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

I tried all possible solutions, I could find, but nothing helped.

Some advice to enable reliable sessions, but it doesn't work with ssl (if only you have your custom binding), others advice to use http instead of https, but I'd like to enable Sessions with my current configurations, if it's possible.

Is there any approach to achieve this?

Any kind of help is much appreciated.

解决方案

If you want "sessions" with wsHttpBinding, you have to use either reliable messaging, or the security sessions. (source : how to enable WCF Session with wsHttpBidning with Transport only Security).

WSHttpBinding supports session but only if either security (SecureConversation) or reliable messaging are enabled. If you are using transport security then it is not using WS-SecureConversation and WS-ReliableMessaging is turned off by default. Therefore the two protocols that WSHttpBinding uses for session are not available. You either need to use message security or turn on reliable session. (source : http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/57b3453e-e7e8-4875-ba23-3be4fff080ea/).

We’ve disallowed RM over Https in the standard bindings because the way to secure an RM session is to use a security session and Https does not provide session.

I found the msdn blurb about it here: http://msdn2.microsoft.com/en-us/library/ms733136.aspx The blurb is "The only exception is when using HTTPS. The SSL session is not bound to the reliable session. This imposes a threat because sessions sharing a security context (the SSL session) are not protected from each other; this might or might not be a real threat depending on the application."

However you can do it if you determine there is no threat. There is an RM over HTTPS sample via custom binding http://msdn2.microsoft.com/en-us/library/ms735116.aspx (source : http://social.msdn.microsoft.com/forums/en-US/wcf/thread/fb4e5e31-e9b0-4c24-856d-1c464bd0039c/).

To sum up your possibilities you can either :

1 - Keep wsHttpBinding, remove transport security and enable reliable messaging

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

But you loose the SSL layer and then part of your security.

2 - Keep wsHttpBinding, keep transport security and add message authentication

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

You get to keep your SSL security layer but your client will have to provide credentials (of any form) and even if you don't validate them at the service side, fake ones must still be provided as WCF will reject any message not specifying credentials.

3 - Use a custom binding with reliable messaging and HTTPS transport

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

I can't see any downside to this except the threat explained in MSDN, and it depends on your application.

4 - Use other session providers If your application is hotsed in IIS, you could set

<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

and depend on the

HttpContext.Current.Session

for your state.

Or implementing your own cookies.

PS : Note that for all those WCF configuration, I only tested service activation, not calls.

EDIT : As per user request, wsHttpBinding with TransportWithMessageCredential security mode implementing sessions (I'm not too familiar with VB.NET so pardon my syntax) :

Service code snippet :

<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

service Configuration snippet :

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

Client test code snippet :

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

这篇关于如何在 WCF 中使用 SSL wsHttpBinding 启用会话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆