一、注册过程说明
这里使用一台海康的摄像头做实际测试。GB28281注册过程有 鉴权 、不鉴权两种,本文实现的带鉴权的方式,基于GB281812016版。本文原本想用C++库实现,但我这只有QT,配置基于第三方sip包的环境太不熟练,花很长时间没搞好,为节省时间暂且用nodejs先代用。
不带鉴权:
带鉴权:
1. 设备设置
2. 注册过程
1. 设备发送register注册消息
2. 服务器返回401 未登陆
3. 设备发送登陆认证
主要是要计算当中的response值,认证计算过程:下面使用kd函数,表示对字符串使用 冒号 拼接后,计算md5,即:如: kd(a,b)=md5(a+”:”+b)kd(a,b) = md5(a + “:” + b)kd(a,b)=md5(a+”:”+b)HA1=kd(username,realm,passwd)HA2=kd(Method,Uri)HA1=kd(username,realm,passwd)HA2=kd(Method,Uri)HA1=kd(username,realm,passwd)HA2=kd(Method,Uri)response有两种情况,一种带nonce,一种不带。
不带nonce:
response=kd(ha1,nonce,ha2)response = kd(ha1,nonce,ha2)response=kd(ha1,nonce,ha2)
带nonce
response=kd(ha1,nonce,nc,cnonce,qop,ha2)response = kd(ha1,nonce,nc,cnonce,qop,ha2)response=kd(ha1,nonce,nc,cnonce,qop,ha2)
4. 服务端校验正确,返回200ok
接下来服务端可以请求设备目录。
本文主要实现REGISTER的部分。
3. 签名校验
二、实现过程
依赖:
1. 启动 sip
sip.start({
logger: {
send: function( message , address ) {
// logger.info("==send==:" , message,address);
},
recv : function(message, address) {
// logger.info("==recv==:" , message,address);
}
}
},function(rq) {
});
1234567891011
返回值解析:
if(rq.method ==='REGISTER') {
logger.info('call register');
var username = sip.parseUri(rq.headers.to.uri).user;
logger.info('register username',username);
var userinfo = registry [username];
//logger.info('userinfo', userinfo);
if(!userinfo) {
// 没有登记的用户,这里直接禁止授权
logger.error('没有登记的用户,这里直接禁止授权:' , username);
var session = {realm: realm};
sip.send(digest.challenge(session, sip.makeResponse(rq, 401, 'Unauthorized')));
return;
}
else {
userinfo.session = userinfo.session || {realm: realm};
if(!digest.authenticateRequest(userinfo.session, rq, {user: username, password: userinfo.password})) {
sip.send(digest.challenge(userinfo.session, sip.makeResponse(rq, 401, 'Unauthorized')));
}
else {
// 完成授权
userinfo. contact = rq.headers.contact;
var rs = sip.makeResponse(rq, 200, 'Ok');
rs.headers.contact = rq.headers.contact;
sip.send(rs);
}
}
}
123456789101112131415161718192021222324252627282930
三、完整代码
var log4js = require('log4js');
log4js.configure({
appenders: {
out: { type: 'stdout' },
app: { type: ' file ', filename: 'application.log' }
},
categories: {
// getLogger 参数为空时,默认使用该分类
default: { appenders: [ 'out', 'app' ], level: 'debug' }
}
});
var logger = log4js.getLogger();
const log4js_extend = require("log4js-extend");
log4js_extend(log4js, {
path: __dirname + "/a.log",
format: "at @name (@file:@line:@column)"
});
var parseString = require('xml2js').parseString;
var sip = require('sip');
var digest = require('sip/digest');
var os = require('os');
var server_account = '34020000002000000001';
var registry = {
'34020000001110000001': {password: '你的设备密码'}
};
debugger;
var realm ='3402000000';
logger.info('localhost name='+realm);
sip.start({
logger: {
send: function(message, address) {
// logger.info("==send==:" , message,address);
},
recv: function(message, address) {
// logger.info("==recv==:" , message,address);
}
}
},
function(rq) {
try {
logger.info('----------------------',rq);
if(rq.method ==='REGISTER') {
logger.info('call register');
var username = sip.parseUri(rq.headers.to.uri).user;
logger.info('register username',username);
var userinfo = registry[username];
//logger.info('userinfo', userinfo);
if(!userinfo) {
// 没有登记的用户,这里直接禁止授权
logger.error('没有登记的用户,这里直接禁止授权:' , username);
var session = {realm: realm};
sip.send(digest.challenge(session, sip.makeResponse(rq, 401, 'Unauthorized')));
return;
}
else {
// 这里应该对server_account再校验一下。但有的网上测试IPC程序server_account没用到uri里。这里先简单实现下原理。
userinfo.session = userinfo.session || {realm: realm};
if(!digest.authenticateRequest(userinfo.session, rq, {user: username, password: userinfo.password})) {
sip.send(digest.challenge(userinfo.session, sip.makeResponse(rq, 401, 'Unauthorized')));
}
else {
// 完成授权
userinfo.contact = rq.headers.contact;
var rs = sip.makeResponse(rq, 200, 'Ok');
rs.headers.contact = rq.headers.contact;
sip.send(rs);
}
}
}
} catch(e) {
logger.error(e);
sip.send(sip.makeResponse(rq, 500, "Server Internal Error"));
}
});
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
正常运行后,可以看到海康摄像头处于在线状态。