5-Onvif协议:IPC客户端之鉴权

参考文章

https://blog.csdn.net/benkaoya/article/details/72477536

https://blog.csdn.net/zhizhengguan/article/details/109325688

一、简介

  • onvif规定,有些接口需要鉴权,有些接口不需要鉴权。例如onvif规定GetDeviceInformation接口时需要鉴权的。(下图为main函数流程分析,标红的api都需要实现鉴权)

    在这里插入图片描述

  • 市面上有些的IPC(网络摄像头),并没有严格的按照ONVIF规范执行,造成客户端不携带鉴权信息也能成功调用某些接口。

    在这里插入图片描述

二、onvif哪些接口需要认证

onvif规定有些接口需要鉴权,有些接口不需要鉴权。那么该怎么知道哪些接口需要认证呢?

在官网的ONVIF-Core-Specification.pdf文档有详细的规定,文档的路径为https://www.onvif.org/specs/core/ONVIF-Core-Specification.pdf,在该文档的**[Access classes for service requests]**章节中有接口访问权限的相关规定,如下图所示:

在这里插入图片描述
在这里插入图片描述

「PRE_AUTH」的规定是:The service shall not require user authentication. Example: GetEndpointReference.(该服务不需要用户认证。例如:GetEndpointReference,即客户端调用该接口时,不需要携带用户名、密码认证信息)。

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

在这里插入图片描述

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

在这里插入图片描述

三、相关数据结构和函数

生成gSOAP框架的代码,项目文件结构和解决编译报错可以参考佬的这篇文章:

https://blog.csdn.net/zhizhengguan/article/details/109325688

1.数据结构

定义在client\application\client.cpp:

鉴权信息包括用户名、密码

USERNAME

PASSWORD

static const char * USERNAME;
static const char * PASSWORD;

2.函数

soap_wsse_add_UsernameTokenDigest()

函数的声明在client\application\wsseapi.h

SOAP_FMAC1 int SOAP_FMAC2 soap_wsse_add_UsernameTokenDigest(struct soap *soap, const char *id, const char *username, const char *password);

函数的定义在client\application\wsseapi.cpp

/**
@fn int soap_wsse_add_UsernameTokenDigest(struct soap *soap, const char *id, const char *username, const char *password)
@brief Adds UsernameToken element for digest authentication.
@param soap context
@param[in] id string for signature referencing or NULL
@param[in] username string
@param[in] password string
@return SOAP_OK

Computes SHA1 digest of the time stamp, a nonce, and the password. The digest
provides the authentication credentials. Passwords are NOT sent in the clear.

@note
This release supports the use of at most one UsernameToken in the header.
*/
SOAP_FMAC1
int
SOAP_FMAC2
soap_wsse_add_UsernameTokenDigest(struct soap *soap, const char *id, const char *username, const char *password)
{
  return soap_wsse_add_UsernameTokenDigest_at(soap, id, username, password, time(NULL));
}
/**
@fn int soap_wsse_add_UsernameTokenDigest_at(struct soap *soap, const char *id, const char *username, const char *password, time_t when)
@brief Adds UsernameToken element for digest authentication.
@param soap context
@param[in] id string for signature referencing or NULL
@param[in] username string
@param[in] password string
@param[in] when the time stamp to use for the digest hash
@return SOAP_OK

Computes SHA1 digest of the time stamp, a nonce, and the password. The digest
provides the authentication credentials. Passwords are NOT sent in the clear.

@note
This release supports the use of at most one UsernameToken in the header.
*/
SOAP_FMAC1
int
SOAP_FMAC2
soap_wsse_add_UsernameTokenDigest_at(struct soap *soap, const char *id, const char *username, const char *password, time_t when)
{
  _wsse__Security *security = soap_wsse_add_Security(soap);
  const char *created = soap_dateTime2s(soap, when);
  char HA[SOAP_SMD_SHA1_SIZE], HABase64[29];
  char nonce[SOAP_WSSE_NONCELEN], *nonceBase64;
  DBGFUN2("soap_wsse_add_UsernameTokenDigest", "id=%s", id?id:"", "username=%s", username?username:"");
  /* generate a nonce */
  soap_wsse_rand_nonce(nonce, SOAP_WSSE_NONCELEN);
  nonceBase64 = soap_s2base64(soap, (unsigned char*)nonce, NULL, SOAP_WSSE_NONCELEN);
  /* The specs are not clear: compute digest over binary nonce or base64 nonce? */
  /* compute SHA1(created, nonce, password) */
  calc_digest(soap, created, nonce, SOAP_WSSE_NONCELEN, password, HA);
  /* Hm...?
     calc_digest(soap, created, nonceBase64, strlen(nonceBase64), password, HA);
   */
  soap_s2base64(soap, (unsigned char*)HA, HABase64, SOAP_SMD_SHA1_SIZE);
  /* populate the UsernameToken with digest */
  soap_wsse_add_UsernameTokenText(soap, id, username, HABase64);
  /* populate the remainder of the password, nonce, and created */
  security->UsernameToken->Password->Type = (char*)wsse_PasswordDigestURI;
  security->UsernameToken->Nonce = (struct wsse__EncodedString*)soap_malloc(soap, sizeof(struct wsse__EncodedString));
  security->UsernameToken->Salt = NULL;
  security->UsernameToken->Iteration = NULL;
  if (!security->UsernameToken->Nonce)
    return soap->error = SOAP_EOM;
  soap_default_wsse__EncodedString(soap, security->UsernameToken->Nonce);
  security->UsernameToken->Nonce->__item = nonceBase64;
  security->UsernameToken->Nonce->EncodingType = (char*)wsse_Base64BinaryURI;
  security->UsernameToken->wsu__Created = soap_strdup(soap, created);
  return SOAP_OK;
}

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

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

  1. 要使用soap_wsse_add_UsernameTokenDigest函数进行授权,在soapcpp2 生成ONVIF代码框架之前,要在onvif.h头文件开头加入(注意是import而不是include):

    #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目录下,将这些文件拷贝到你的项目中,以便参与编译。

    打开wsse.h,可以看到相应依赖
    在这里插入图片描述
    注意:依赖一定要自己去看,否则会出现一大堆未定义引用。

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

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

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


四、鉴权功能实现

void setUSERNAME(const char *username)
{
    USERNAME = username;

#if DEBUG
    std::cout << "-------------------------setUSERNAME-------------------------" << "\n";
    std::cout << "USERNAME = "<< USERNAME << "\n";
    std::cout << "-------------------------------------------------------------" << "\n";
#endif

}

void setPASSWORD(const char *password)
{
    PASSWORD = password;

#if DEBUG
    std::cout << "-------------------------setPASSWORD-------------------------" << "\n";
    std::cout << "PASSWORD = "<< password << "\n";
    std::cout << "-------------------------------------------------------------" << "\n";
#endif

}

/************************************************************************
**函数: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(nullptr != username);
            SOAP_ASSERT(nullptr != password);

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

    EXIT:

    return result;
}
Android Onvif鉴权是指在Android设备上进行Onvif协议通信时的身份验证和访问控制。 首先,Onvif是一种开放的网络视频接口标准,旨在提供设备和客户端之间的互操作性。在Android设备上使用Onvif时,鉴权是非常重要的,以确保只有经过授权的用户才能访问设备。 在Android上进行Onvif鉴权时,通常需要使用用户名和密码进行身份验证。这些凭据可以用于验证用户是否具有访问权限。当用户尝试连接到设备时,首先会提供用户名和密码进行身份验证,如果凭据正确,才能继续访问设备的功能和服务。 鉴权的另一个重要方面是访问控制。通过鉴权,管理员可以管理用户的访问权限。在Android设备上,可以设置不同级别的用户权限,例如管理员、操作员和观察者。管理员有最高权限,可以访问和控制设备的所有功能,操作员具有较低的权限,只能执行一部分功能,而观察者则只能查看设备上的视频流。通过访问控制,可以确保只有经过授权的用户能够在Android设备上执行特定的操作。 在实现Android Onvif鉴权时,开发者需要使用支持Onvif协议的库和API。这些库和API提供了与Onvif设备进行通信的各种功能和方法,包括鉴权和访问控制。 总而言之,Android Onvif鉴权是确保在Android设备上进行Onvif协议通信时进行身份验证和访问控制的过程。它通过用户名和密码进行身份验证,并使用访问控制来管理用户权限,以确保只有经过授权的用户才能访问设备的功能和服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值