.net 带有 wsHttpBinding 且没有 Windows 安全性的 WCF 会话

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4767102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 15:09:53  来源:igfitidea点击:

WCF sessions with a wsHttpBinding and without windows security

.netwcfwcf-securitywcf-bindingwcf-sessions

提问by Robert

I need to create a WCF service that is hosted in IIS, uses http transport and hold state in the server's memory. While I'm aware that stateful services aren't a good idea, this last constrain is necessary to make the service work with a legacy client.

我需要创建一个托管在 IIS 中的 WCF 服务,在服务器的内存中使用 http 传输和保持状态。虽然我知道有状态服务不是一个好主意,但最后一个约束是使服务与旧客户端一起工作所必需的。

My first thought was to asp.net's session to store the values. I activated the asp.net compatibility mode in my service, which gave me access to the HttpContext, but values that were placed in the session object were not being persisted in memory. I assume this was because the http module that handles session state was not correctly configured, but when googling for answer I came across, WCF sessions and thought it might be a better idea to use them.

我的第一个想法是通过asp.net 的会话来存储值。我在我的服务中激活了 asp.net 兼容模式,这使我可以访问 HttpContext,但是放置在会话对象中的值没有保留在内存中。我认为这是因为处理会话状态的 http 模块没有正确配置,但是当我在谷歌上搜索答案时,我遇到了 WCF 会话,并认为使用它们可能是一个更好的主意。

However, WCF sessions seem what under-document and place a strange set of prerequises on a service, and I haven't been able to find a configuration that suits my needs: must be hosted in IIS, must use http or https transport and can't reply on windows authentication because the client and server will not be part of the same domain. I'm trying to get this going using the wsHttpBinding, I had heard WCF sessions required either security or reliable message, but: - Using the standard binding and when the servers are not part of the same domain it fails with a “SecurityNegotiationException The caller was not authenticated by the service” exception. This is fairly logical as it was using windows security.

但是,WCF 会话似乎记录不足,并在服务上放置了一组奇怪的先决条件,而且我无法找到适合我需要的配置:必须托管在 IIS 中,必须使用 http 或 https 传输并且可以' 不回复 Windows 身份验证,因为客户端和服务器将不是同一域的一部分。我正在尝试使用 wsHttpBinding 来实现这一点,我听说 WCF 会话需要安全性或可靠消息,但是: - 使用标准绑定并且当服务器不属于同一域时,它会失败并显示“SecurityNegotiationException The caller未通过服务进行身份验证”异常。这是相当合乎逻辑的,因为它使用的是 Windows 安全性。

  • If I disable security complete it fails with a “Contract requires Session, but Binding 'WSHttpBinding' doesn't support it or isn't configured properly to support it.”

  • If while keeping security disabled I enable reliable message I get the exception “Binding validation failed because the WSHttpBinding does not support reliable sessions over transport security (HTTPS). The channel factory or service host could not be opened. Use message security for secure reliable messaging over HTTP.”

  • I've tried enabling transport level security but this doesn't seem to make any difference to the error generated

  • 如果我禁用安全完成,它会失败并显示“合同需要会话,但绑定 'WSHttpBinding' 不支持它或未正确配置以支持它。”

  • 如果在禁用安全性的同时启用可靠消息,我会收到异常“绑定验证失败,因为 WSHttpBinding 不支持传输安全性 (HTTPS) 上的可靠会话。无法打开通道工厂或服务主机。使用消息安全性通过 HTTP 实现安全可靠的消息传递。”

  • 我试过启用传输级安全性,但这似乎对生成的错误没有任何影响

Is there any configuration that might work for me? Or should I just go back to the plan of using asp.net sessions?

有没有适合我的配置?或者我应该回到使用asp.net会话的计划?

回答by Allon Guralnek

You can have WCF hold session information in memory in a pretty simple way. To eliminate any possible external influences in my instructions, I'll assume you're starting with a brand new project:

您可以让 WCF 以一种非常简单的方式将会话信息保存在内存中。为了消除我的说明中任何可能的外部影响,我假设您从一个全新的项目开始:

  1. Create a new WCF Service Library project. This project will already contain a service with a WSHttpBidingbinding preconfigured.
  2. Go to the service contract (IService1.cs) and change the ServiceContract attribute to the following:

    [ServiceContract(SessionMode = SessionMode.Required)]
    
  3. Go to the service implimentation (Service1.cs) and add the following ServiceBehavior attribute to the service class (Service1):

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
    
  4. Add session data as members of the service class (Service1):

    public class Service1 : IService1
    {
        ...
    
        private string UserFullName { get; set; }
    
        ...
    }
    
  5. Use the members to present session specific data (remember to also add them to the service contract, IService1):

    public class Service1 : IService1
    {
        ...
    
        public string Welcome(string fullName)
        {
            UserFullName = fullName ?? "Guest";
            return string.Format("Welcome back, {0}!", UserFullName);
        }
    
        public string Goodbye()
        {
            return string.Format("Come back soon, {0}!", UserFullName ?? "Guest");
        }
    
        ...
    }
    
  1. 创建一个新的 WCF 服务库项目。该项目已经包含一个WSHttpBiding预先配置了绑定的服务。
  2. 转到服务合同 (IService1.cs) 并将 ServiceContract 属性更改为以下内容:

    [ServiceContract(SessionMode = SessionMode.Required)]
    
  3. 转到服务实现 (Service1.cs) 并将以下 ServiceBehavior 属性添加到服务类 ( Service1):

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
    
  4. 添加会话数据作为服务类 ( Service1) 的成员:

    public class Service1 : IService1
    {
        ...
    
        private string UserFullName { get; set; }
    
        ...
    }
    
  5. 使用成员来呈现特定于会话的数据(记住还要将它们添加到服务合同中IService1):

    public class Service1 : IService1
    {
        ...
    
        public string Welcome(string fullName)
        {
            UserFullName = fullName ?? "Guest";
            return string.Format("Welcome back, {0}!", UserFullName);
        }
    
        public string Goodbye()
        {
            return string.Format("Come back soon, {0}!", UserFullName ?? "Guest");
        }
    
        ...
    }
    

SessionMode.Requiredensures that your clients are session-tracked.
InstanceContextMode.PerSessionensures that an instance of your service class (Service1) is created for every session, so that you can retain session data in it and it will exist in memory across multiple calls in the same session.
ConcurrencyMode.Singleensures that only one thread can enter each service class instance (Service1), and prevents possible concurrency issues if you only access data from the service class (and external thread-safe locations).

SessionMode.Required确保您的客户得到会话跟踪。
InstanceContextMode.PerSession确保为每个会话创建服务类 (Service1) 的实例,以便您可以在其中保留会话数据,并且它会在同一会话中的多个调用中存在于内存中。
ConcurrencyMode.Single确保只有一个线程可以进入每个服务类实例 (Service1),并且如果您仅从服务类(和外部线程安全位置)访问数据,则可以防止可能出现的并发问题。

EDIT:By default, WSHttpBindingonly allows security sessions. But it also support reliable sessions, which allow establishing sessions without security enabled. The following binding configuration disables security and enables reliable sessions:

编辑:默认情况下,WSHttpBinding只允许安全会话。但它也支持可靠会话,允许在不启用安全性的情况下建立会话。以下绑定配置禁用安全并启用可靠会话:

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

回答by mythz

IMO this is what happens when you're using a technology with a poor abstraction over HTTP like WCF. The fact that WCF web services could in theory be hosted without HTTP (i.e. over NET TCP, MSMQ, etc) just makes it hard to use built-in features of HTTP without entering in configuration hell and start a game of "guess the correct configuration by trial and error" where you try every possible configuration permutation until you've found the correct one that works!

IMO 这就是当您使用像 WCF 这样的 HTTP 抽象较差的技术时会发生的情况。WCF Web 服务理论上可以在没有 HTTP 的情况下托管(即通过 NET TCP、MSMQ 等)这一事实使得很难在不进入配置地狱的情况下使用 HTTP 的内置功能并开始“猜测正确的配置”的游戏通过反复试验”,您可以尝试所有可能的配置排列,直到找到正确的配置排列!

Ultimately if you couldn't use WCF and had to implement the web service from scratch you would simply set a cookie when the client successfully authenticated. Then with every client request just grab the session information referenced by that cookie.

最终,如果您不能使用 WCF 并且必须从头开始实现 Web 服务,您只需在客户端成功通过身份验证时设置一个 cookie。然后对于每个客户端请求,只需获取该 cookie 引用的会话信息。

One possible solution if you had to use WCF is to take session management in your own hands (It's what I do when I'm unhappy with the effort required to get something to work)and have an explicit 'Session' property on all your web services that require a session/authentication (usually a guid generated on Authentication). So for each subsequent request you use the guid to rehydrate the session information associated with that client.

如果您必须使用 WCF,一种可能的解决方案是自己掌握会话管理(当我对工作所需的努力不满意时,我会这样做)并在您的所有网络上拥有一个明确的“会话”属性需要会话/身份验证的服务(通常是在身份验证时生成的 guid)。因此,对于每个后续请求,您都使用 guid 来重新组合与该客户端关联的会话信息。

If you're interested in trying out different web service frameworks I maintain an Open Source Web Services Frameworkthat lets you build configuration-free, DRY, testable web services where (without any configuration required) each web service you create is automatically accessible over REST XML, JSON, JSV, SOAP 1.1, SOAP 1.2 endpoints. Effectively it allows you to access your same web service via a HTTP GET url for REST-ful clients and easy debugging as well as SOAP endpoints (a popular choice still mandated by some enterprises). The Hello Worldtutorial should give you a good overview on some of its features and how it works.

如果您有兴趣尝试不同的 Web 服务框架,我会维护一个开源 Web 服务框架,它可以让您构建免配置、DRY、可测试的 Web 服务,其中(无需任何配置)您创建的每个 Web 服务都可以通过 REST 自动访问XML、JSON、JSV、SOAP 1.1、SOAP 1.2 端点。实际上,它允许您通过 HTTP GET url 为 REST-ful 客户端和简单的调试以及 SOAP 端点(一些企业仍然强制要求的流行选择)访问相同的 Web 服务。在的Hello World教程应该给你它的一些特性以及它是如何工作的一个很好的概述。