具有 wsHttpBinding 且没有 Windows 安全性的 WCF 会话

发布于 2024-10-13 15:10:53 字数 872 浏览 4 评论 0原文

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

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

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

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

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

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

没有可能适合我的配置?或者我应该回到使用 ASP.NET 会话的计划?

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.

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.

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.

  • 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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一身骄傲 2024-10-20 15:10:53

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

  1. 创建一个新的 WCF 服务库项目。该项目已包含一个预先配置了 WSHttpBiding 绑定的服务。
  2. 转到服务契约 (IService1.cs) 并将 ServiceContract 属性更改为以下内容:

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

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

    公共类Service1:IService1
    {
        ...
    
        私有字符串 UserFullName { 获取;放; }
    
        ...
    }
    
  5. 使用成员来呈现会话特定数据(请记住还将它们添加到服务契约中, IService1):

    公共类Service1:IService1
    {
        ...
    
        公共字符串欢迎(字符串全名)
        {
            用户全名 = 全名 ?? “客人”;
            return string.Format("欢迎回来,{0}!", UserFullName);
        }
    
        公共字符串再见()
        {
            return string.Format("请尽快回来,{0}!", UserFullName ?? "Guest");
        }
    
        ...
    }
    

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

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

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

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:

  1. Create a new WCF Service Library project. This project will already contain a service with a WSHttpBiding binding 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");
        }
    
        ...
    }
    

SessionMode.Required ensures that your clients are session-tracked.
InstanceContextMode.PerSession ensures 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.Single ensures 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).

EDIT: By default, WSHttpBinding only 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>
    <binding name="wsHttpBindingConfiguration">
        <security mode="None" />
        <reliableSession enabled="true" />
    </binding>
</wsHttpBinding>
夏夜暖风 2024-10-20 15:10:53

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

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

如果您必须使用 WCF,一种可能的解决方案是自己进行会话管理(当我对某些工作所需的工作不满意时,我就是这样做的)并有一个明确的“所有需要会话/身份验证的 Web 服务上的 Session 属性(通常是在身份验证上生成的 guid)。因此,对于每个后续请求,您都可以使用 guid 来补充与该客户端关联的会话信息。

如果您有兴趣尝试不同的 Web 服务框架,我维护一个开源 Web 服务框架,可让您构建配置-免费、DRY、可测试的 Web 服务(无需任何配置)您创建的每个 Web 服务都可以通过 REST XML、JSON、JSV、SOAP 1.1、SOAP 1.2 端点自动访问。实际上,它允许您通过 REST 式客户端的 HTTP GET url 访问相同的 Web 服务,并轻松调试以及 SOAP 端点(一些企业仍然强制执行的流行选择)。 Hello World 教程应该可以让您很好地了解它的一些功能及其工作原理。

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!

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.

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.

If you're interested in trying out different web service frameworks I maintain an Open Source Web Services Framework that 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 World tutorial should give you a good overview on some of its features and how it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文