2017-04-07 111 views
0

我正在开发使用.NET 4.0(Windows窗体,而不是WCF)一个ONVIF驱动。 我开始在Visual Studio中将WSDL文件作为服务导入。 所以我能够发送命令到设备这种方式:ONVIF WSDL服务:无法验证

HttpTransportBindingElement httpTransportBindingElement = new HttpTransportBindingElement(); 
[...] 

TextMessageEncodingBindingElement messegeElement = new TextMessageEncodingBindingElement(); 
[...] 
CustomBinding binding = new CustomBinding(messegeElement, httpTransportBindingElement); 
[...] 

EndpointAddress serviceAddress = new EndpointAddress(url); 

DeviceClient deviceClient = new DeviceClient(binding, serviceAddress); 

Device channel = deviceClient.ChannelFactory.CreateChannel(); 

DeviceServiceCapabilities dsc = channel.GetServiceCapabilities(); 

但我不能够管理HTTP摘要认证。我花了几天的时间搜索谷歌的例子和解决方案,但唯一的办法似乎是手写XML代码。没有任何干净的解决方案,如:

deviceClient.ChannelFactory.Credentials.HttpDigest.ClientCredential.UserName = USERNAME; 
deviceClient.ChannelFactory.Credentials.HttpDigest.ClientCredential.Password = digestPassword; 

(这不起作用)?

回答

0

对于未来的读者,我终于能够在不使用WSE 3.0执行这两种类型的身份验证。 这部分代码(急促),基于IClientMessageInspector接口上(你可以找到很多基于该接口的其他例子):

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
    { 
     if (HTTPDigestAuthentication) 
     { 
      string digestHeader = string.Format("Digest username=\"{0}\",realm=\"{1}\",nonce=\"{2}\",uri=\"{3}\"," + 
               "cnonce=\"{4}\",nc={5:00000000},qop={6},response=\"{7}\",opaque=\"{8}\"", 
               _username, realm, nonce, new Uri(this.URI).AbsolutePath, cnonce, counter, qop, digestResponse, opaque); 

      HttpRequestMessageProperty httpRequest = new HttpRequestMessageProperty(); 
      httpRequest.Headers.Add("Authorization", digestHeader); 
      request.Properties.Add(HttpRequestMessageProperty.Name, httpRequest); 

      return Convert.DBNull; 
     } 
     else if (UsernametokenAuthorization) 
     { 
      string headerText = "<wsse:UsernameToken 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:Username>" + _username + "</wsse:Username>" + 
           "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest\">" + digestPassword + "</wsse:Password>" + 
           "<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" + Convert.ToBase64String(nonce) + "</wsse:Nonce>" + 
           "<wsu:Created xmlns=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" + created + "</wsu:Created>" + 
           "</wsse:UsernameToken>"; 

      XmlDocument MyDoc = new XmlDocument(); 
      MyDoc.LoadXml(headerText); 

      MessageHeader myHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", MyDoc.DocumentElement, false); 

      request.Headers.Add(myHeader); 

      return Convert.DBNull; 
     } 

     return request; 
    } 
+0

很多东西都是从你的榜样缺失,和一些非常重要的东西......你怎么了用于格式化摘要头部的变量? (?例如现时你需要做出一个请求?) – cube45

+0

是的,你是对的:你必须发送一个请求,无需认证,服务器将包含随机数,境界等的错误信息回答。 – ElmoDev001

+0

你是否在BeforeSendRequest的开头创建了自己的HttpClient?你能编辑你的答案并提供一个更完整的例子吗? http://findnerd.com/list/view/How-to-resolve-Http-400-Bad-Request-while-authenticating-with-ONVIF-camera/: – cube45

1

首先,你应该安装Microsoft.Web.Services3包。 (查看>其他窗口>程序包管理器控制台)。然后,您必须将摘要行为添加到您的端点。代码的第一部分是PasswordDigestBehavior类,之后用于连接到ONVIF设备服务。

public class PasswordDigestBehavior : IEndpointBehavior 
{ 
    public String Username { get; set; } 
    public String Password { get; set; } 

    public PasswordDigestBehavior(String username, String password) 
    { 
     this.Username = username; 
     this.Password = password; 
    } 


    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
     // do nothing 
    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
    { 
     //clientRuntime.MessageInspectors.Add(new PasswordDigestMessageInspector(this.Username, this.Password)); 
     clientRuntime.MessageInspectors.Add(new PasswordDigestMessageInspector(this.Username, this.Password)); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 
    { 
     throw new NotImplementedException(); 
    } 

    public void Validate(ServiceEndpoint endpoint) 
    { 
     // do nothing... 
    } 
} 


public class PasswordDigestMessageInspector : IClientMessageInspector 
{ 
    public String Username { get; set; } 
    public String Password { get; set; } 

    public PasswordDigestMessageInspector(String username, String password) 
    { 
     this.Username = username; 
     this.Password = password; 
    } 

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
    { 
     // do nothing 
    } 

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
    { 
     // Use the WSE 3.0 security token class 
     var option = PasswordOption.SendHashed; 
     if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password)) 
      option = PasswordOption.SendPlainText; 

     UsernameToken token = new UsernameToken(this.Username, this.Password, option); 

     // Serialize the token to XML 
     XmlDocument xmlDoc = new XmlDocument(); 
     XmlElement securityToken = token.GetXml(xmlDoc); 

     // find nonce and add EncodingType attribute for BSP compliance 
     XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); 
     nsMgr.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); 
     XmlNodeList nonces = securityToken.SelectNodes("//wsse:Nonce", nsMgr); 
     XmlAttribute encodingAttr = xmlDoc.CreateAttribute("EncodingType"); 
     encodingAttr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; 
     if (nonces.Count > 0) 
     { 
      nonces[0].Attributes.Append(encodingAttr); 
      //nonces[0].Attributes[0].Value = "foo"; 
     } 


     // 
     MessageHeader securityHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", securityToken, false); 
     request.Headers.Add(securityHeader); 

     // complete 
     return Convert.DBNull; 
    } 
} 

这是如何使用它:

var endPointAddress = new EndpointAddress("http://DEVICE_IPADDRESS/onvif/device_service"); 
      var httpTransportBinding = new HttpTransportBindingElement { AuthenticationScheme = AuthenticationSchemes.Digest }; 
      var textMessageEncodingBinding = new TextMessageEncodingBindingElement { MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None) }; 
      var customBinding = new CustomBinding(textMessageEncodingBinding, httpTransportBinding); 
      var passwordDigestBehavior = new PasswordDigestBehavior(USERNAME, PASSWORD); 
      var deviceService = new DeviceClient(customBinding, endPointAddress); 
      deviceService.Endpoint.Behaviors.Add(passwordDigestBehavior); 
+0

您的解决方案工作,但正在执行的SOAP验证,而不是HTTP摘要式身份验证。无论如何,最终我能够在不使用WSE 3.0的情况下执行这两种身份验证。 – ElmoDev001