.net 如何添加 SOAP 安全标头

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19111773/
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 12:29:05  来源:igfitidea点击:

how to add SOAP Security header

.netweb-servicessoapcredentials

提问by E-A

I've read a lot of articles and answers but I couldn't work it out.

我已经阅读了很多文章和答案,但我无法解决。

I'm using .NET Framework 4.0 on my project. So, I first added the WebService as a Service Reference and get the bindings on my app.config. And I'm going to be listing my attempts

我在我的项目中使用 .NET Framework 4.0。因此,我首先将 WebService 添加为服务引用,并在我的 app.config 上获取绑定。我将列出我的尝试



Attempt #1

尝试 #1

I instantiated the service like this and added the credentials:

我像这样实例化了服务并添加了凭据:

On App.Config

在 App.Config 上

<binding name="SubscriptionWSImplServiceSoapBinding" >
  <security mode="TransportWithMessageCredential">
    <transport clientCredentialType="Basic"/>
  </security>
</binding>

SubscriptionWSImplServiceClient ClientWs = new SubscriptionWSImplServiceClient();
ClientWs.ClientCredentials.UserName.UserName = "some-username";
ClientWs.ClientCredentials.UserName.Password = "some-password";

var SomeResposne = ClientWs.RunMethod(); //I have exception at this point

I get an Exception as below:

我收到如下异常:

 System.ArgumentException: The provided URI scheme 'http' is invalid;
 expected 'https'. Parameter name: via at System.ServiceModel.Channels

The exceptions says that the web service's url must be HTTPS but the given web service url to me is HTTP not HTTPS.

例外情况是 Web 服务的 url 必须是 HTTPS,但给我的给定 Web 服务 url 是 HTTP 而不是 HTTPS。

But I did not mind the HttpS and went for my 2nd attempt.

但我并不介意 HttpS 并进行了第二次尝试。



Attempt #2

尝试#2

As stated in this answerI tried as below:

this answer所述, 我尝试如下:

BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
CustomBinding customBinding = new CustomBinding(binding);
SecurityBindingElement element = customBinding.Elements.Find<SecurityBindingElement>();
element.IncludeTimestamp = false;

EndpointAddress address = new EndpointAddress("https://www.GivenServiceUrl.com/WebService"); //Note that the url does not end as ..svc or ..asmx! Just the name like this with no dot or extension


SubscriptionWSImplServiceClient ClientWs = new SubscriptionWSImplService(customBinding, address);
ClientWs.ClientCredentials.UserName.UserName = "some-username";
ClientWs.ClientCredentials.UserName.Password = "some-password";

var SomeResposne = ClientWs.RunMethod(); //I have exception at this point

I get an Exception as below:

我收到如下异常:

System.ServiceModel.Security.SecurityNegotiationException: Could not establish trust relationship for the SSL/TLS secure channel with authority 'https://www.GivenServiceUrl.com/WebService'

System.ServiceModel.Security.SecurityNegotiationException:无法为具有权限“ https://www.GivenServiceUrl.com/WebService”的 SSL/TLS 安全通道建立信任关系

Therefore, I went for my 3rd attempt which I gave the reference as WebReference not as ServiceReference on my project.

因此,我进行了第三次尝试,在我的项目中,我将参考作为 WebReference 而不是 ServiceReference。



Attempt #3

尝试 #3

SubscriptionWSImplService ClientWs2 = new SubscriptionWSImplService();
var SomeResposne = ClientWs.RunMethod(); //I have exception at this point

I don't know how to send the security header at this point.

我现在不知道如何发送安全标头。

I get an Exception as below:

我收到如下异常:

 System.Web.Services.Protocols.SoapHeaderException: An error was
 discovered processing the <wsse: Security> header

But in here, I could not add the necessary header. Could you help me with this?

但是在这里,我无法添加必要的标题。你能帮我解决这个问题吗?



Attempt 1 or Attempt 2 or Attempt 3, which one should be used also considering that I use Framework 4.0? And how can I solve the problem?

尝试 1 或尝试 2 或尝试 3,考虑到我使用 Framework 4.0,应该使用哪一个?我该如何解决这个问题?

According to document the final request SOAP must be looking like this:

根据文档,最终请求 SOAP 必须如下所示:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sub="http://subscription.services.ws.fourplay.com.tr/">
   <soapenv:Header>
      <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
         <wsse:UsernameToken wsu:Id="UsernameToken-12" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsse:Username>admin</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">123456</wsse:Password>
           <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">1lcj+WbCMlrPyhcud4QxiQ==</wsse:Nonce>
           <wsu:Created>2012-06-05T10:23:26.879Z</wsu:Created>
        </wsse:UsernameToken>
     </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <sub:RunMethod>
         <!--Optional:-->
         <sub:opaque id>555555555</sub:opaque id>
         <sub:params>
            <!--Optional:-->
            <name>GUID</name>
            <!--Optional:-->
            <value>2fc4ce1d-645e-41f4-811e-28510a02a17f </value>
         </sub:params>      
</sub: RunMethod >

采纳答案by E-A

I solved it by using WCF without any credentials declared. I did it by building my custom header I got help from this link

我在没有声明任何凭据的情况下使用 WCF 解决了这个问题。我通过构建我的自定义标头来做到这一点 我从这个链接得到了帮助

public class SoapSecurityHeader : MessageHeader
    {
        private readonly string _password, _username, _nonce;
        private readonly DateTime _createdDate;

        public SoapSecurityHeader(string id, string username, string password, string nonce)
        {
            _password = password;
            _username = username;
            _nonce = nonce;
            _createdDate = DateTime.Now;
            this.Id = id;
        }

        public string Id { get; set; }

        public override string Name
        {
            get { return "Security"; }
        }

        public override string Namespace
        {
            get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
        }

        protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            writer.WriteStartElement("wsse", Name, Namespace);
            writer.WriteXmlnsAttribute("wsse", Namespace);
        }

        protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            writer.WriteStartElement("wsse", "UsernameToken", Namespace);
            writer.WriteAttributeString("Id", "UsernameToken-10");
            writer.WriteAttributeString("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");

            writer.WriteStartElement("wsse", "Username", Namespace);
            writer.WriteValue(_username);
            writer.WriteEndElement();

            writer.WriteStartElement("wsse", "Password", Namespace);
            writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
            writer.WriteValue(_password);
            writer.WriteEndElement();

            writer.WriteStartElement("wsse", "Nonce", Namespace);
            writer.WriteAttributeString("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
            writer.WriteValue(_nonce);
            writer.WriteEndElement();

            writer.WriteStartElement("wsse", "Created", Namespace);
            writer.WriteValue(_createdDate.ToString("YYYY-MM-DDThh:mm:ss"));
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }

and to how to use the header I got it from this link.

以及如何使用我从这个链接得到的标题。

回答by Waqas ali

I have had the same issue as the First Attempt of the question and I needed to have a output same as the question. The answer is perfectly fine but Just to improve on the above answer or i should say combine the answer with the link i did the the following.

我遇到了与问题的第一次尝试相同的问题,我需要有与问题相同的输出。答案非常好,但只是为了改进上述答案,或者我应该说将答案与链接结合起来,我做了以下操作。

 public class Security : MessageHeader
{
    private readonly string _password, _username, _nonce;
    private readonly DateTime _createdDate;

    public Security(string id, string username, string password, string nonce)
    {
        _password = password;
        _username = username;
        _nonce = nonce;
        _createdDate = DateTime.Now;
        this.Id = id;
    }

    public string Id { get; set; }

    public override string Name => "Security";

    public override string Namespace => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

    protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteStartElement("wsse", Name, Namespace);
        writer.WriteXmlnsAttribute("wsse", Namespace);
    }

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteStartElement("wsse", "UsernameToken", Namespace);
        writer.WriteAttributeString("Id", "UsernameToken-10");
        writer.WriteAttributeString("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");

        writer.WriteStartElement("wsse", "Username", Namespace);
        writer.WriteValue(_username);
        writer.WriteEndElement();

        writer.WriteStartElement("wsse", "Password", Namespace);
        writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
        writer.WriteValue(_password);
        writer.WriteEndElement();

        writer.WriteStartElement("wsse", "Nonce", Namespace);
        writer.WriteAttributeString("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
        writer.WriteValue(_nonce);
        writer.WriteEndElement();

        writer.WriteStartElement("wsse", "Created", Namespace);
        writer.WriteValue(_createdDate.ToString("YYYY-MM-DDThh:mm:ss"));
        writer.WriteEndElement();

        writer.WriteEndElement();
    }
}
/// <summary>
/// Represents a message inspector object that can be added to the <c>MessageInspectors</c> collection to view or modify messages.
/// </summary>
public class ClientMessageInspector : IClientMessageInspector
{
    /// <summary>
    /// Enables inspection or modification of a message before a request message is sent to a service.
    /// </summary>
    /// <param name="request">The message to be sent to the service.</param>
    /// <param name="channel">The WCF client object channel.</param>
    /// <returns>
    /// The object that is returned as the <paramref name="correlationState " /> argument of
    /// the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply(System.ServiceModel.Channels.Message@,System.Object)" /> method.
    /// This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid" /> to ensure that no two
    /// <paramref name="correlationState" /> objects are the same.
    /// </returns>
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
///enter your username and password here.
        Security header = new Security("xx", "xx", "xx!","xx");

        request.Headers.Add(header);
        return header.Name;
    }

    /// <summary>
    /// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
    /// </summary>
    /// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
    /// <param name="correlationState">Correlation state data.</param>
    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {

    }
}

/// <summary>
/// Represents a run-time behavior extension for a client endpoint.
/// </summary>
public class CustomEndpointBehavior : IEndpointBehavior
{
    /// <summary>
    /// Implements a modification or extension of the client across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that is to be customized.</param>
    /// <param name="clientRuntime">The client runtime to be customized.</param>
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(new ClientMessageInspector());
    }

    /// <summary>
    /// Implement to pass data at runtime to bindings to support custom behavior.
    /// </summary>
    /// <param name="endpoint">The endpoint to modify.</param>
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implements a modification or extension of the service across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that exposes the contract.</param>
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implement to confirm that the endpoint meets some intended criteria.
    /// </summary>
    /// <param name="endpoint">The endpoint to validate.</param>
    public void Validate(ServiceEndpoint endpoint)
    {
        // Nothing special here
    }
}