GB28181学习笔记3 实现REGISTER设备注册(基于nodejs sip库 SERVER端)

一、注册过程说明

这里使用一台海康的摄像头做实际测试。
GB28281注册过程有鉴权、不鉴权两种,本文实现的带鉴权的方式,基于GB281812016版。
本文原本想用C++库实现,但我这只有QT,配置基于第三方sip包的环境太不熟练,为节省时间暂且用nodejs先代用。

不带鉴权:
在这里插入图片描述

带鉴权:
在这里插入图片描述

1. 设备设置

在这里插入图片描述

2. 注册过程

1. 设备发送register注册消息

在这里插入图片描述

2. 服务器返回401 未登陆

在这里插入图片描述

3. 设备发送登陆认证

在这里插入图片描述
主要是要计算当中的response值,认证计算过程:
下面使用kd函数,表示对字符串使用 冒号 拼接后,计算md5,即:
如: k d ( a , b ) = m d 5 ( a + " : " + b ) kd(a,b) = md5(a + ":" + b)
H A 1 = k d ( u s e r n a m e , r e a l m , p a s s w d ) H A 2 = k d ( M e t h o d , U r i ) HA1=kd(username,realm,passwd) HA2=kd(Method,Uri)
response有两种情况,一种带nonce,一种不带。

不带nonce:

r e s p o n s e = k d ( h a 1 , n o n c e , h a 2 ) response = kd(ha1,nonce,ha2)

带nonce

r e s p o n s e = k d ( h a 1 , n o n c e , n c , c n o n c e , q o p , h a 2 ) response = kd(ha1,nonce,nc,cnonce,qop,ha2)

4. 服务端校验正确,返回200ok

在这里插入图片描述

接下来服务端可以请求设备目录。
整体描述:
在这里插入图片描述
本文主要实现REGISTER的部分。

3. 签名校验

二、实现过程

依赖:
https://github.com/kirm/sip.js

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) {
});

返回值解析:

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);
        }
      }
    }

三、完整代码


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"));
  }
});



正常运行后,可以看到海康摄像头处于在线状态。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xundh/article/details/106285119