linux系统下,onvif设备鉴权实现

开发环境

Fedora23
qtcreator4.0.3 based on Qt5.6.2
opnessl

前言

onvif设备鉴权,其实很简单,但是作者在开发过程中走了很大的弯路,尝试了多种方法,最终都以失败告终。曾经一度陷入绝望,甚至产生回家种地的念头。最终功夫不负有心人,发现其实一个函数就可以搞定,颇有柳暗花明又一村的感觉。或许这就是编程的魅力所在吧。

简介

在网上搜索相关资料,发现主要有两种方式:一种是利用soap中提供的soap_wsse_add_UsernameTokenDigest(soap, "user", ONVIF_USERNAME, ONVIF_PASSWORD);函数;另一种是自己重写密码摘要生成函数。本文采用第一种方式,其中ONVIF_USERNAME和ONVIF_PASSWORD是登陆摄像机所需的用户名和密码。此外,这种方法还需要在获取onvif协议框架代码时,在onvif.h文件中手动添加import "wsse.h"语句。详情请参考:
http://blog.csdn.net/my_lianlian/article/details/56013162

准备工作

在进行设备鉴权之前,必须要确认系统中是否正确安装openssl。此处感谢所有开源软件开发者,Fedora23安装系统时自带了openssl,所以只需要直接调用即可。如果读者的系统中没有openssl,请先安装。

设备鉴权

废话不多说,以下以获取设备能力功能为例,介绍如何实现设备鉴权。
首先添加获取设备能力函数:

void UserGetCapabilities(struct soap *soap, struct __wsdd__ProbeMatches *resp, struct _tds__GetCapabilities *capa_req, struct _tds__GetCapabilitiesResponse *capa_resp)
{
    capa_req->Category = (enum tt__CapabilityCategory*)soap_malloc(soap, sizeof(int));
    capa_req->__sizeCategory = 1;
    *(capa_req->Category) = (enum tt__CapabilityCategory)(tt__CapabilityCategory__Media);
    capa_resp->Capabilities = (struct tt__Capabilities*)soap_malloc(soap, sizeof(struct tt__Capabilities));
    soap_wsse_add_UsernameTokenDigest(soap, "user", "admin", "waycom12345");

    printf("\n--------------Now Getting Capabilities ----------------\n\n");
    int result = soap_call___tds__GetCapabilities(soap, resp->wsdd__ProbeMatches->ProbeMatch->XAddrs, NULL, capa_req, capa_resp);
    printf("[%d] xAddr is %s\n", __LINE__, resp->wsdd__ProbeMatches->ProbeMatch->XAddrs);
    if(soap->error)
    {
        printf("[%s][%d]--->>> soap error: %d, %s, %s\n", __func__, __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
        int retval = soap->error;
        exit(-1);
    }
    else
    {
        printf("\n---------------GetCapabilities OK! result = %d-------------------\n\n", result);
        if(capa_resp->Capabilities == NULL)
        {

            printf("GetCapabilities failed! result = %d\n", result);
        }
        else
        {
            printf("Media->Xaddr = %s \n", capa_resp->Capabilities->Media->XAddr);
        }
    }
}

获取能力函数参考文章:
http://blog.csdn.net/gubenpeiyuan/article/details/25618177?utm_source=tuicool&utm_medium=referral

在上一篇文章main.cpp中添加以上代码,编译报错:

‘soap_wsse_add_UsernameTokenDigest’was not declared in this scope

查找发现,该函数声明在/usr/local/gsoap/share/gsoap/plugin/wsseapi.h中
将该文件添加至工程中,编译。发现该文件依赖:
wsseapi.cpp smdevp.h smdevp.c mecevp.h mecevp.c threads.h threads.c
这些文件全部都在/usr/local/gsoap/share/gsoap/plugin文件夹中。

全部添加完毕后,编译,程序报错:

invalid conversion from ‘void*’ to ‘int()(int, X509_STORE_CTX){aka int…

出现该问题原因是没有添加对openssl的调用和缺少相关宏的调用。
进入工程所在路径,使用vi命令编辑Makefile文件。
在CXXFLAGS参数最后添加-DWITH_DOM -DWITH_OPENSSL,预定义这两个宏;
在LIBS参数最后添加 -lssl -lcrypto,添加对openssl的调用。
编译,发现有函数未定义,原因是未添加dom.cpp文件。该文件存在于gsoap安装包中的gsoap文件夹中,将dom.cpp拷贝到工程目录下并添加进工程中。编译,程序报出

undefined reference to ‘soap_rand’

等错误,进入stdsoap2.h文件中,找到soap_rand()函数的定义之处:

#if defined(WITH_OPENSSL)
# define soap_random soap_rand()
 SOAP_FMAC1 int SOAP_FMAC2 soap_rand(void);
#elif defined(HAVE_RANDOM)
# define soap_random (int)random()
#else
# define soap_random rand()
#endif

在第三行后面添加一个空行,编译通过。
运行程序,发现

[UserGetCapabilities][23]—>>soap error: 4, (null), (null)

这一问题困扰作者良久,一直认为鉴权失败,但苦于没有调试信息,不知道如何去定位错误位置。最终参考网上一位高手文章,问题得以解决。文章链接:
http://blog.csdn.net/bing87496988/article/details/38707829
解决方法:
进入stdsoap2.cpp中,找到

soap_s2byte(struct soap *soap, const char *s, char *p)
{ if (s)
  { long n;
    char *r;
    n = soap_strtol(s, &r, 10);
    if (/*s == r || *r ||*/ n < -128 || n > 127)
      soap->error = SOAP_TYPE;
    *p = (char)n;
  }
  return soap->error;
}

参照以上内容,注释掉/*s == r || *r ||*/部分即可。
编译、运行,成功获取设备能力。

你可能感兴趣的:(onvif)