C# WCF 服务,读取soapenv 头安全部分

问题描述

我正在寻找一种解决方法来读取传入的 soapenv => 安全部分。

我一直试图从传入的请求中获取soapenv,但没有成功:

int operationIndex = OperationContext.Current.IncomingMessageHeaders.FindHeader("Security","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
string operation = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(operationIndex);

这是一个关于如何格式化标题的示例:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
  <soapenv:Header>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wsse:Usernametoken wsu:Id="Usernametoken-1">
            <wsse:Username>TheUsername</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">XXXXXXXXXXXXXXXX</wsse:Password>
            <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">XXXXXXXXXXXXX</wsse:Nonce>
            <wsu:Created>XXXXXXXXXX</wsu:Created>
        </wsse:Usernametoken>
        </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <tem:AMethod>
         <!--Optional:-->
         <tem:field>2</tem:field>
      </tem:AMethod>
   </soapenv:Body>
</soapenv:Envelope>

还有其他方法可以读取传入的soapheader吗?

编辑

刚刚找到了另一种获取soapheader的方法

var headerContent = OperationContext.Current.IncomingMessageHeaders.Select((value,i) => new { i,value }).ToList().Where(x => x.value.Name.ToLower().Equals("security")).FirstOrDefault();
XmlDictionaryReader xr = OperationContext.Current.IncomingMessageHeaders.GetReaderAtHeader(headerContent.i);
string xmlSecurityHeader = xr.ReadOuterXml();

解决方法

可以试试messageinspector,客户端实现IClientMessageInspector接口,服务端实现IDispatchMessageInspector接口。您可以拦截请求和回复。

这是客户端上的示例:

     public class ClientMessageLogger : IClientMessageInspector
{
    public void AfterReceiveReply(ref Message reply,object correlationState)
    {
        string outputstr = $"Server reply message received by the client:\n{reply}\n";
        Console.WriteLine(outputstr);
    }

    public object BeforeSendRequest(ref Message request,IClientChannel channel)
    {
        string outputText = $"The request message that the client will send:\n{request}\n";
        Console.WriteLine(outputText);
        return null;

    }
}
[AttributeUsage(AttributeTargets.Interface)]
public class CustomBehavior : Attribute,IContractBehavior
{
    public Type TargetContract => typeof(ServiceReference1.ICalculator);
    public void AddBindingParameters(ContractDescription contractDescription,ServiceEndpoint endpoint,BindingParameterCollection bindingParameters)
    {
        return;
    }

    public void ApplyClientBehavior(ContractDescription contractDescription,ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(new ClientMessageLogger());
    }
    public void ApplyDispatchBehavior(ContractDescription contractDescription,DispatchRuntime dispatchRuntime)
    {
        return;
    }

    public void Validate(ContractDescription contractDescription,ServiceEndpoint endpoint)
    {
        return;
    }
}

这是服务器上的示例:

public class CustomMessageInspector : IDispatchMessageInspector
    {
        public object AfterReceiveRequest(ref Message request,IClientChannel channel,InstanceContext instanceContext)
        {
            string displayText = $"The request message received by the server:\n{request}\n";
            Console.WriteLine(displayText);
            return null;
        }

        public void BeforeSendReply(ref Message reply,object correlationState)
        {
            string displayText = $"Server reply message:\n{reply}\n";
            Console.WriteLine(displayText);
        }
    }
    [AttributeUsage(AttributeTargets.Interface)]
    public class CustomBehavior : Attribute,IContractBehavior
    {
        public void AddBindingParameters(ContractDescription contractDescription,BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyClientBehavior(ContractDescription contractDescription,ClientRuntime clientRuntime)
        {
            return;
        }
        public void ApplyDispatchBehavior(ContractDescription contractDescription,DispatchRuntime dispatchRuntime)
        {
            dispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
        }

        public void Validate(ContractDescription contractDescription,ServiceEndpoint endpoint)
        {
            return;
        }
    }

最终,在接口上方添加 [CustomBehavior],在客户端,接口在您的服务引用中。