【问题标题】:how to add SOAP Security header如何添加 SOAP 安全标头
【发布时间】:2013-10-01 08:48:07
【问题描述】:

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

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


尝试 #1

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

在 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

我得到如下异常:

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

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

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


尝试 #2

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

我得到如下异常:

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

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


尝试 #3

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

此时我不知道如何发送安全标头。

我得到如下异常:

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

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


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

根据文档,最终请求 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 >

【问题讨论】:

  • 您将无法通过 Web 参考来执行此操作。它们不支持 WS-Security

标签: .net web-services soap credentials


【解决方案1】:

我通过使用 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();
        }
    }

以及如何使用我从 link 获得的标题。

【讨论】:

  • 当你说你“使用过 WCF”时......你到底是什么意思?我想我只熟悉向 wsdl 添加“Web 引用”。我们是否将 VS 创建的肥皂代理转换为 WCF 服务或其他什么?
  • 不要使用“添加 Web 引用”。那是遗留的“ASMX”技术,不应该用于新的开发。
  • 您的 _createdDate.ToString(...) 使用了无效的格式字符串,该字符串会生成像“YYYY-10-DDT06:10:59”这样的日期...而不是使用 _createdDate.ToString("o" )
  • 链接已损坏。
【解决方案2】:

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

 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
    }
}

【讨论】:

    【解决方案3】:

    优秀的帖子。你们都救了我的命!!我必须进行一些更改才能获得以下标题:

    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xml:mustUnderstand="1">
        <wsse:UsernameToken wsu:Id="UsernameToken-15" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsse:Username>EFO140714JPA</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">SgBJAHMANwBBACYANQBOAG8ANwAzACEANgBrAGEAJgBIAGwAJABMAA==</wsse:Password>
            <wsse:Nonce>SgBJAHMANwBBACYANQBOAG8ANwAzACEANgBrAGEAJgBIAGwAJABMAA==</wsse:Nonce>
            <wsu:Created>2016-08-04T11:24:10.0Z</wsu:Created>
        </wsse:UsernameToken>
    </wsse:Security>
    

    我所做的更改如下:

            public class SoapSecurityHeader : MessageHeader
            {
                private readonly string _password, _username, _nonce, _createdDate;
    
                public SoapSecurityHeader(string id, string username, string password, string nonce, string created)
                {
                    _password = password;
                    _username = username;
                    _nonce = nonce;
                    _createdDate = created;
                    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);
     ------->       writer.WriteXmlAttribute("mustUnderstand", "1");
                }
    
    
                protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
                {
                    writer.WriteStartElement("wsse", "UsernameToken", Namespace);
                    writer.WriteAttributeString("wsu:Id", "UsernameToken-15");
                    writer.WriteAttributeString("xmlns: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#PasswordDigest");
                    writer.WriteValue(_password);
                    writer.WriteEndElement();
    
                    writer.WriteStartElement("wsse", "Nonce", Namespace);
                    writer.WriteValue(_nonce);
                    writer.WriteEndElement();
    
      ------>       writer.WriteStartElement("wsu:Created");
                    writer.WriteValue(_createdDate);
                    writer.WriteEndElement();
    
                    writer.WriteEndElement();
                }
            }
    

    【讨论】:

      猜你喜欢
      • 2011-08-15
      • 1970-01-01
      • 1970-01-01
      • 2019-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-06
      相关资源
      最近更新 更多