onvif 鉴权最佳实践
《上一篇文章》讲了如何gsoap生成代码。但是生成以后使用时还有一些问题要注意。如鉴权:常用的有WS-Username_token、Digest鉴权.
wsseapi.h里面默认包含了WS-Username_token鉴权方式,使用比较简单
重点说下Digest鉴权方式:
需要用到httpda.h,httpda.c文件引入项目。原理是:
1.先请求获取认证序列号及相关参数
2.填入用户名密码进行Digest验证
与WS-Username_token不同的地方是:在HTTP协议认证选项中填写认证参数。而后者则是在XML节点(报文体)中填写认证参数
完整鉴权代码:
T -- 业务逻辑代理类
T1 -- 请求参数类
T2 -- 对应请求参数的应答类
call -- T的成员函数(参数1为T1指针,参数2为T2引用)
template <class T, class T1, class T2>
int BindingRequest(T* t, T1* t1, T2& t2, int (T::* call)(T1*, T2&), const std::string& hostname, const std::string& userid, const std::string& passwd) {
t->soap_endpoint = hostname.c_str();
if (SOAP_OK == soap_wsse_add_UsernameTokenDigest(t->soap, nullptr, userid.c_str(), passwd.c_str())) {
if (SOAP_OK != (t->*call)(t1, t2)) {
if (t->soap->status == 400) {
//WS-Username Token鉴权失败,转入Digest鉴权
soap_register_plugin(t->soap, http_da);
if (SOAP_OK != (t->*call)(t1, t2)) {
if (t->soap->status == 401)
{
//首次Digest鉴权返回401是正常的,此时可以获取到Digest的几个参数项,此时再填写用户名和密码,进行二次请求
struct http_da_info httpDaInfo = { 0 }; // to store userid and passwd
//soap.authrealm在验证失败从服务器返回
http_da_save(t->soap, &httpDaInfo, t->soap->authrealm, userid.c_str(), passwd.c_str());
if (SOAP_OK != (t->*call)(t1, t2)) {
std::cerr << "Oops, something went wrong:" << std::endl;
soap_stream_fault(t->soap, std::cerr);
}
http_da_restore(t->soap, &httpDaInfo);
http_da_release(t->soap, &httpDaInfo);
}
}
}
}
}
return t->soap->status;
}
使用案例:
void cleanup_soap(struct soap* soap) {
if (soap != nullptr) {
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
}
}
//获取设备信息
void getDeviceInformation() {
DeviceBindingProxy proxy(soap_new());
_tds__GetDeviceInformation GetDeviceInfo = {};
_tds__GetDeviceInformationResponse GetDeviceInformationResponse = {};
BindingRequest<DeviceBindingProxy, _tds__GetDeviceInformation, _tds__GetDeviceInformationResponse>(
&proxy, &GetDeviceInfo, GetDeviceInformationResponse, &DeviceBindingProxy::GetDeviceInformation, hostname, username, password);
if (proxy.soap->status == 200)
{
std::cout << "Manufacturer: " << GetDeviceInformationResponse.Manufacturer << std::endl;
std::cout << "Model: " << GetDeviceInformationResponse.Model << std::endl;
std::cout << "FirmwareVersion: " << GetDeviceInformationResponse.FirmwareVersion << std::endl;
std::cout << "SerialNumber: " << GetDeviceInformationResponse.SerialNumber << std::endl;
std::cout << "HardwareId: " << GetDeviceInformationResponse.HardwareId << std::endl;
}
cleanup_soap(proxy.soap);
}