无法读取 WCF 服务中的标头

发布于 2024-10-05 04:34:43 字数 5236 浏览 0 评论 0原文

我正在尝试添加一个自定义安全功能,其中客户端向 Silverlight 客户端进行的每个服务调用添加一个令牌,然后该服务可以访问此令牌来控制应用程序安全系统。我尝试通过实现 IClientMessageInspector 接口并将其链接到我生成的服务客户端来实现此目的。我没有使用 Visual Studio 生成的代理,而是使用 ChannelFactory 创建的我自己的客户端。我设法在网上找到了几种解决方案,它们似乎都使用两种基本方法之一。首先,将标头添加到呈现给 IClientMessageInspector.BeforeSendRequest 的消息的 headers 集合中,其次使用 HttpRequestMessageProperty 将信息作为属性添加到呈现给 IClientMessageInspector.BeforeSendRequest 的消息。我一直在尝试这两种方法,但没有成功。看来这两种技术都成功地将数据添加到请求中,但我无法在服务器上访问这两种技术。我想补充一点,这对我来说是一个非常新的领域,由于缺乏经验,我很可能错过了互联网上的答案。

生成我的客户端的代码是:

    private ISecurityAdministrationContract CreateChannel()
    {
        if (factory == null)
        {
            lock (this)
            {
                // Create a custom binding that uses HTTP and binary encoding.
                var elements = new List<BindingElement>();
                elements.Add(new BinaryMessageEncodingBindingElement());
                elements.Add(new HttpTransportBindingElement());
                var binding = new CustomBinding(elements);

                // Create a channel factory for the service endpoint configured with the custom binding.
                factory = new ChannelFactory<ISecurityAdministrationContract>(binding, new EndpointAddress(SecurityAdminServiceAddress));

                //Add my IClientMessageInspector
                factory.Endpoint.Behaviors.Add(new ClientSecurityInterceptor());
            }                
        }
        ISecurityAdministrationContract client = factory.CreateChannel();
        return client;
    }
        }
        ISecurityAdministrationContract client = factory.CreateChannel();
        return client;
    }

将我的标头添加到请求的代码是:

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        //Method 1
        MessageHeader header = MessageHeader.CreateHeader("MyFirstAuthentication", "ns", "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEEE");
        request.Headers.Add(header);

        //Method 2
        HttpRequestMessageProperty httpRequestMessage;
        httpRequestMessage = new HttpRequestMessageProperty();
        httpRequestMessage.Headers["MySecondAuthentication"] = "11111111-2222-3333-4444-5555555555555";
        request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);

        return null;
    }

上面的代码实现了这两种技术。

fiddler 捕获的服务调用是:(注意 http 被 ht_tp 替换,因为该网站不允许我发布 hyoerlinks)

POST ht_tp://127.0.0.1:6785/SecurityAdministrationRelayService.svc/ HTTP/1.1 接受:/ 参考:ht_tp://ipv4.fiddler:6785/ClientBin/Civica.Housing.xap 接受语言: en-gb 内容长度:400 内容类型:application/soap+msbin1 我的第二次身份验证:11111111-2222-3333-4444-5555555555555 接受编码:gzip、deflate 用户代理:Mozilla/4.0(兼容;MSIE 8.0;Windows NT 5.1;Trident/4.0;GTB5;InfoPath.2;.NET CLR 3.0.04506.648;.NET CLR 3.5.21022;.NET CLR 3.0.4506.2152;.NET CLR 3.5.30729;.NET4.0E;.NET CLR 2.0.50727) 主机:127.0.0.1:6785 连接:保持活动状态 编译指示:无缓存

V_ s _a_V_D �Ahttp://tempuri.org/ISecurityAdministrationContract/CreateAdvocateD���_�9�HJ��9��-��D,D*�@MyFirstAuthentication_ns�%AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEEED _���@http://ipv4.fiddler:6785/SecurityAdministrationRelayService.svc/V@_CreateAdvocate_http://tempuri.org/@ AdvocateVO�{"Id":0,"UserId":4,"AdvocateForId":8,"ValidFrom":"/Date(1291127869690+0000)/","ValidTo":null}__

这似乎包含这两种技术的令牌信息。

当我尝试提取此信息时,服务器出现问题。我已经实现了 IOperationInvoker 并尝试在 IncomingMessageHeaders 中查找标头,但没有成功。我还在 IncomingMessageProperties 中进行了搜索,但看不到任何添加的标头详细信息。 这是我在 IOperationInvoker 中使用的代码:

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        PreInvoke(instance, inputs);
        object returnedValue = null;
        object[] outputparams = new object[] { };
        Exception exception = null;
        try
        {
            returnedValue = originalInvoker.Invoke(instance, inputs, out outputparams);
            outputs = outputparams;
            return returnedValue;
        }
        catch (Exception e)
        {
            exception = e; throw;
        }
        finally
        {
            PostInvoke(instance, returnedValue, outputparams, exception);
        }
    }


    protected virtual void PreInvoke(object instance, object[] inputs)
    {
        //Look for header directly
        int index = System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.FindHeader("MyFirstAuthentication", "ns");

        //Search via enumerator
        foreach (var header in System.ServiceModel.OperationContext.Current.IncomingMessageHeaders)
        {
        }

    }

FindHeader 返回 -1,而枚举器找到 5 个标头,即; 'Action'、'MessageID'、'ReplyTo'、'VsDebuggerCausalityData' 和'到'。

OperationContext.Current.IncomingMessageProperties 集合包含 4 个条目,即:“Via”、“Security”、“Encoder”和“Encoder”。 'System.ServiceModel.Channels.RemoteEndpointMessageProperty'

事实上,如果我注释掉客户端中将 IClientMessageInspector 添加到 ClienChannel 的行,那么 fiddler 报告的 http 会通过省略添加的详细信息进行更改,但传入消息上的标头和属性集合没有变化。

任何关于我如何访问此信息或为什么它不作为传入消息的一部分呈现的想法将非常感激。

I am trying to add a custom security feature where the client adds a token to each service call made by a Silverlight client and then the service can access this token to control an application security system. I am attempting to do this by implementing the IClientMessageInspector interface and linking this in to my generated service client. I am not using the Visual studio generated proxy but my own client created by a ChannelFactory. I have managed to find several solutions on the web which all seem to use one of 2 basic methods. Firstly by adding a header to the headers collection of the Message presented to the IClientMessageInspector.BeforeSendRequest, and secondly by using an HttpRequestMessageProperty to add the info as a property to the Message presented to the IClientMessageInspector.BeforeSendRequest. I have been trying both of these methods without any success. It appears that both techniques successfully add data to the request but I have been unable to access either at the server. I would add that this is a very new area for me and it is very possible that I have missed the answer on the internet because of inexperience.

The code to generate my client is:

    private ISecurityAdministrationContract CreateChannel()
    {
        if (factory == null)
        {
            lock (this)
            {
                // Create a custom binding that uses HTTP and binary encoding.
                var elements = new List<BindingElement>();
                elements.Add(new BinaryMessageEncodingBindingElement());
                elements.Add(new HttpTransportBindingElement());
                var binding = new CustomBinding(elements);

                // Create a channel factory for the service endpoint configured with the custom binding.
                factory = new ChannelFactory<ISecurityAdministrationContract>(binding, new EndpointAddress(SecurityAdminServiceAddress));

                //Add my IClientMessageInspector
                factory.Endpoint.Behaviors.Add(new ClientSecurityInterceptor());
            }                
        }
        ISecurityAdministrationContract client = factory.CreateChannel();
        return client;
    }
        }
        ISecurityAdministrationContract client = factory.CreateChannel();
        return client;
    }

The code to add my header to the request is:

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        //Method 1
        MessageHeader header = MessageHeader.CreateHeader("MyFirstAuthentication", "ns", "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEEE");
        request.Headers.Add(header);

        //Method 2
        HttpRequestMessageProperty httpRequestMessage;
        httpRequestMessage = new HttpRequestMessageProperty();
        httpRequestMessage.Headers["MySecondAuthentication"] = "11111111-2222-3333-4444-5555555555555";
        request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);

        return null;
    }

The above code implements both techniques.

The service call trapped by fiddler is: (NB http replaced with ht_tp cos the site won't allow me to post hyoerlinks)

POST ht_tp://127.0.0.1:6785/SecurityAdministrationRelayService.svc/ HTTP/1.1
Accept: /
Referer: ht_tp://ipv4.fiddler:6785/ClientBin/Civica.Housing.xap
Accept-Language: en-gb
Content-Length: 400
Content-Type: application/soap+msbin1
MySecondAuthentication: 11111111-2222-3333-4444-5555555555555
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB5; InfoPath.2; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727)
Host: 127.0.0.1:6785
Connection: Keep-Alive
Pragma: no-cache

V_
s
_a_V_D
���Ahttp://tempuri.org/ISecurityAdministrationContract/CreateAdvocateD�ߔ_�9�HJ��9��-��D,D*�@MyFirstAuthentication_ns�%AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEEED
_���@http://ipv4.fiddler:6785/SecurityAdministrationRelayService.svc/V@_CreateAdvocate_http://tempuri.org/@
advocateVO�
{"Id":0,"UserId":4,"AdvocateForId":8,"ValidFrom":"/Date(1291127869690+0000)/","ValidTo":null}__

This does seem to contain the token info for both of the techniques.

The problem comes at the server when I try to extract this info. I have implemented an IOperationInvoker and tried to find the header in the IncomingMessageHeaders without any success. I have also searched in the IncomingMessageProperties but I cannot see any of my added header detail.
This is the code I am using in the IOperationInvoker:

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        PreInvoke(instance, inputs);
        object returnedValue = null;
        object[] outputparams = new object[] { };
        Exception exception = null;
        try
        {
            returnedValue = originalInvoker.Invoke(instance, inputs, out outputparams);
            outputs = outputparams;
            return returnedValue;
        }
        catch (Exception e)
        {
            exception = e; throw;
        }
        finally
        {
            PostInvoke(instance, returnedValue, outputparams, exception);
        }
    }


    protected virtual void PreInvoke(object instance, object[] inputs)
    {
        //Look for header directly
        int index = System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.FindHeader("MyFirstAuthentication", "ns");

        //Search via enumerator
        foreach (var header in System.ServiceModel.OperationContext.Current.IncomingMessageHeaders)
        {
        }

    }

The FindHeader returns -1 whilst the enumerator finds 5 headers, namely; ‘Action’, ‘MessageID’, ‘ReplyTo’, ‘VsDebuggerCausalityData’ & ‘To’.

The OperationContext.Current.IncomingMessageProperties collection contains 4 entries, namely: ‘Via’, ‘Security’, ‘Encoder’ & ‘System.ServiceModel.Channels.RemoteEndpointMessageProperty’

In fact if I comment out the line in my client that adds the IClientMessageInspector to the ClienChannel then the http that fiddler reports changes by omitting the added detail, but the header and property collections on the Incoming message are unchanged.

Any ideas as to how I can access this info, or why it is not being presented as part of the IncomingMessage would be very gratefully received.

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

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

发布评论

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

评论(3

┈┾☆殇 2024-10-12 04:34:43

我已经回答了我自己的问题。上面的代码没有任何问题,只需链接到正确的位置即可。在我的完整解决方案中,我从 Silverlight 客户端调用一个服务,然后该客户端调用第二个服务。令人尴尬的是,我已将 IOperationInvoker 链接到第二个服务(doh)。当我将其链接到中间服务时,标头按预期可用。

读取该值的代码是:

            int index = OperationContext.Current.IncomingMessageHeaders.FindHeader("MyFirstAuthentication", "ns");
            if (index >= 0)
            {
                string value = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index);
            }

I've answered my own question. There is nothing wrong with any of the above code, it just needs to be linked into the correct place. In my full solution I am calling a service from Silverlight client which then calls a second service. Embarrassingly I had linked my IOperationInvoker into the second service(doh). When I linked it in to the intermediate service the header was available as expected.

The code to read the value is:

            int index = OperationContext.Current.IncomingMessageHeaders.FindHeader("MyFirstAuthentication", "ns");
            if (index >= 0)
            {
                string value = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index);
            }
微暖i 2024-10-12 04:34:43

您可以使用以下命令来访问标头值:

var headers = OperationContext.Current.IncomingMessageProperties["httpRequest"];
var apiToken = ((HttpRequestMessageProperty)headers).Headers["<YourHeaderKey>"];

You can use the following to access a header value:

var headers = OperationContext.Current.IncomingMessageProperties["httpRequest"];
var apiToken = ((HttpRequestMessageProperty)headers).Headers["<YourHeaderKey>"];
后来的我们 2024-10-12 04:34:43

较短的版本:

string value = OperationContext.Current.
               IncomingMessageHeaders.GetHeader<string>("MyFirstAuthentication", "ns");

Shorter version:

string value = OperationContext.Current.
               IncomingMessageHeaders.GetHeader<string>("MyFirstAuthentication", "ns");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文