ONVIF协议网络摄像机(IPC)客户端程序开发(9):鉴权(认证)

1 专栏导读

本专栏第一篇文章「专栏开篇」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解,专栏前面文章讲过的知识点(或代码段),后面文章不会赘述。为了节省篇幅,突出重点,在文章中展示的示例代码仅仅是关键代码,你可以在「专栏开篇」中获取完整代码。

如有错误,欢迎你的留言纠正!让我们共同成长!你的「点赞」「打赏」是对我最大的支持和鼓励!

2 前言

接着上一篇文章接续,上篇文章说到,在测试ONVIF标准的GetDeviceInformation接口时,有些IPC要求鉴权(认证),有些IPC不需要。其实总结起来应该是这样:

  1. ONVIF规定,有些接口需要鉴权,有些接口不需要鉴权(在哪里查询等下会说明)。ONVIF规定GetDeviceInformation接口是需要鉴权的。

  2. 市面上的IPC摄像头,特别是山寨货,并没有严格按ONVIF规范执行,造成对于有的IPC,客户端不携带鉴权信息也能成功调用GetDeviceInformation接口。

3 ONVIF哪些接口需要认证

辣么,问题来了,哪些ONVIF接口需要鉴权,哪些不需要鉴权,在哪里可以查看呢?答案是,在官网的ONVIF Core Specification文档中有详细的规定,如Version 16.12的版本为《ONVIF-Core-Specification-v1612.pdf》。在该文档的「Access classes for service requests」章节中有接口访问权限的相关规定,如下图所示。比如「PRE_AUTH」的规定是:The service shall not require user authentication,那非「PRE_AUTH」的就是都要认证的。


图1

拿GetServices接口举个例子,在ONVIF Core Specification文档中找到GetServices接口定义(如下图所示),会有Access Class: PRE_AUTH的说明,表明客户端调用该接口时,不需要携带用户名、密码认证信息。


图2

再看看GetDeviceInformation接口规定(如下图所示),Access Class: READ_SYSTEM,说明客户端调用该接口是需要进行认证。


图3

4 如何认证

这里的鉴权信息包括用户名、密码,在HTTP传输过程中不能是明文,有一定的加密算法。

如果你整不清楚这个加密算法怎么回事,那么,我推荐利用gSOAP源码中的soap_wsse_add_UsernameTokenDigest函数,可以轻松实现鉴权。要使用该函数,需要注意以下几点:

  1. 就像专栏前面文章提到过的一样,要使用soap_wsse_add_UsernameTokenDigest函数进行授权,在soapcpp2 生成ONVIF代码框架之前,要在onvif.h头文件开头加入:

    #import “wsse.h”

  2. 依赖gSOAP中的这些源码:wsseapi.c、wsseapi.h、mecevp.c、mecevp.h、smdevp.c、smdevp.h、threads.c、threads.h、dom.c,这些文件在gsoap目录和gsoap/plugin目录下,将这些文件拷贝到你的项目中,以便参与编译。

  3. 在加入上面说的.c和.h文件后,结果编译失败,提示找不到「openssl/evp.h」文件:

    smdevp.h(54): fatal error C1083: 无法打开包括文件:“openssl/evp.h”: No such file or directory

    究其原因,wsse系列函数依赖OpenSSL库,我们得去OpenSSL官网下载源代码来编译、安装,里面有我们需要的库文件和头文件。

5 安装OpenSSL

如果你是跟着我专栏在学习,在专栏前面文章「使用gSOAP生成ONVIF框架代码」中,已经安装过OpenSSL了,对于还没安装OpenSSL的,可以参考那篇文章如何安装。

6 实现认证

代码如下,重点在ONVIF_SetAuthInfo函数(是对soap_wsse_add_UsernameTokenDigest的二次封装),相比于上一篇文章,这次的ONVIF_GetDeviceInformation函数内部,增加了设置鉴权信息,在调用soap_call___tds__GetDeviceInformation之前,先调用ONVIF_SetAuthInfo函数设置鉴权信息。

你可以拿一个需要鉴权的IPC来测试,通过开启、关闭ONVIF_SetAuthInfo语句,来观察效果。注意:你要是拿一个本就不需要鉴权的IPC,是测不出预期效果的。

#define USERNAME    "admin"
#define PASSWORD    "admin"

/************************************************************************
**函数:ONVIF_SetAuthInfo
**功能:设置认证信息
**参数:
        [in] soap     - soap环境变量
        [in] username - 用户名
        [in] password - 密码
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
static int ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password)
{
    int result = 0;

    SOAP_ASSERT(NULL != username);
    SOAP_ASSERT(NULL != password);

    result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);
    SOAP_CHECK_ERROR(result, soap, "add_UsernameTokenDigest");

EXIT:

    return result;
}

/************************************************************************
**函数:ONVIF_GetDeviceInformation
**功能:获取设备基本信息
**参数:
        [in] DeviceXAddr - 设备服务地址
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_GetDeviceInformation(const char *DeviceXAddr)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _tds__GetDeviceInformation           devinfo_req;
    struct _tds__GetDeviceInformationResponse   devinfo_resp;

    SOAP_ASSERT(NULL != DeviceXAddr);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    memset(&devinfo_req, 0x00, sizeof(devinfo_req));
    memset(&devinfo_resp, 0x00, sizeof(devinfo_resp));
    result = soap_call___tds__GetDeviceInformation(soap, DeviceXAddr, NULL, &devinfo_req, &devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "GetDeviceInformation");

    dump_tds__GetDeviceInformationResponse(&devinfo_resp);

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }
    return result;
}

用大华IPC进行测试,执行结果如下:

================= + dump_tds__GetDeviceInformationResponse + >>>
Manufacturer:       Dahua
Model:              IPC-HDW1025C
Serial Number:      2K04788PAA00441
Hardware Id:        1.00
Firmware Version:   2.420.Dahua 00.14.R, build: 2016-06-18
================= - dump_tds__GetDeviceInformationResponse - <<<

7 特别注意

需要特别、特别、特别注意的是:但凡是ONVIF规定要鉴权的接口,每次调用之前,都要重新设置一次鉴权信息(即调用ONVIF_SetAuthInfo函数),哪怕你之前已经设置过鉴权信息了,否则后续调用ONVIF接口依然会报错。

我们来做个测试,连续两次调用soap_call___tds__GetDeviceInformation接口,第一次调用之前有设置鉴权信息,第二次调用之前没有设置鉴权信息,代码如下:

int ONVIF_GetDeviceInformation2(const char *DeviceXAddr)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _tds__GetDeviceInformation           devinfo_req;
    struct _tds__GetDeviceInformationResponse   devinfo_resp;

    SOAP_ASSERT(NULL != DeviceXAddr);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    /* 第一次调用soap_call___tds__GetDeviceInformation之前有设置鉴权信息 */
    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    memset(&devinfo_req, 0x00, sizeof(devinfo_req));
    memset(&devinfo_resp, 0x00, sizeof(devinfo_resp));
    result = soap_call___tds__GetDeviceInformation(soap, DeviceXAddr, NULL, &devinfo_req, &devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "GetDeviceInformation");
    dump_tds__GetDeviceInformationResponse(&devinfo_resp);


    /* 第二次调用soap_call___tds__GetDeviceInformation之前没有设置鉴权信息,导致调用失败 */
    memset(&devinfo_req, 0x00, sizeof(devinfo_req));
    memset(&devinfo_resp, 0x00, sizeof(devinfo_resp));
    result = soap_call___tds__GetDeviceInformation(soap, DeviceXAddr, NULL, &devinfo_req, &devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "GetDeviceInformation");
    dump_tds__GetDeviceInformationResponse(&devinfo_resp);

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }
    return result;
}

同样用大华IPC进行测试,执行结果如下,第二次调用soap_call___tds__GetDeviceInformation失败:

================= + dump_tds__GetDeviceInformationResponse + >>>
Manufacturer:       Dahua
Model:              IPC-HDW1025C
Serial Number:      2K04788PAA00441
Hardware Id:        1.00
Firmware Version:   2.420.Dahua 00.14.R, build: 2016-06-18
================= - dump_tds__GetDeviceInformationResponse - <<<

[soap] GetDeviceInformation error: 401, is internal, HTTP Error

之所以会如此,究其根源,是因为IPC的应答信息会重置soap对象,导致鉴权信息丢失,所示每次都要重新设置鉴权信息。

  • 24
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 34
    评论
ONVIF协议是一种开放的网络视频接口标准,旨在实现网络摄像机网络视频录像机和其他相关设备的互通互联。在开发ONVIF协议网络摄像机客户端程序时,我们需要遵循以下步骤: 首先,为了开发ONVIF协议网络摄像机客户端程序,我们需要对ONVIF协议进行深入了解。ONVIF协议定义了网络摄像机和客户端之间的通信规则和接口,包括设备发现、视频流传输、设备控制等。我们需要熟悉协议的各个方面,包括消息结构、命令和响应等。 其次,我们需要选择合适的开发平台和工具。ONVIF协议可以在不同平台上运行,如Windows、Linux等。我们可以选择适合我们的开发环境的编程语言和工具,并研究它们的ONVIF协议支持。 接下来,我们需要编写客户端程序的代码。首先,我们需要实现设备发现功能,通过发送协议指定的消息,搜索网络中的ONVIF设备。一旦找到设备,我们就可以获取设备的信息,如设备地址、设备型号等。然后,我们可以向设备发送命令,如实时视频流请求、设备时间同步等。我们需要处理设备返回的响应消息,并根据协议规范处理各种结果。 最后,我们需要对开发客户端程序进行测试和调试。我们可以创建模拟的ONVIF设备,模拟设备的各种行为,并验证客户端程序的功能和性能。我们还可以使用一些专业的测试工具来检查客户端程序的兼容性和稳定性。 总而言之,在开发ONVIF协议网络摄像机客户端程序时,我们需要深入了解ONVIF协议,选择适合的开发平台和工具,编写代码实现协议的各种功能,并进行测试和调试。这个过程中需要耐心和细心,以确保开发出高质量的客户端程序
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值