七叶笔记 » golang编程 » GB28181学习笔记3 设备注册Server(基于nodejs sip库)

GB28181学习笔记3 设备注册Server(基于nodejs sip库)

一、注册过程说明

这里使用一台海康的摄像头做实际测试。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  

正常运行后,可以看到海康摄像头处于在线状态。

相关文章