【认证】Http Digest摘要认证 Web实现 vue、node(http + crypto)

目录

需求背景

技术栈

实现过程逻辑 

代码信息

最终效果

优化思考  (摘要认证客户端实现已做优化,优化文章地址见 https://blog.csdn.net/Harvay/article/details/112020107 )


需求背景

开发客户端工具,实现本地PC客户端登录操作海康设备并做相关操作的能力支持,经调研了解有两种实现方式:

1、 C++、Java、C#等传统客户端实现手段结合设备SDK进行操作支持;(该方式支持所有的海康设备接入)

2、 通过Web手段结合Http摘要认证进行实现。(该方式需要设备端支持摘要方式认证方可进行设备接入等操作)

需求方项目上均采用近年海康设备,目前海康主推ISAPI协议方式对接设备,考虑Web技术栈的开发效率以及团队人员技术栈分布情况,采用方式2实现需求方需求。

 

技术栈

electron + vue 实现客户端, node http + axios 做服务端以及设备对接的http客户端支撑。

 

实现过程逻辑 

摘要认证大致过程: 

1、 客户端发去没有认证证书的请求,获取服务端的认证信息反馈:

2、 服务器产生随机数,并将相关信息至于响应中 www-Authenticate字段中, 用于后续客户端获取做进一步认证信息提交:

其中若设备支持多种认证方式,则对应多个www-Authenticate响应头信息。

3、 客户端接收401响应后,选择算法(md5)生成消息摘要,并将摘要信息填入请求头的Authorization字段中,发送服务端校验信息:

4、 服务端接收摘要信息后,选择算法以及掌握的数据,进行校验,校验通过后放行数据。

 

代码信息

业务代码 ---调用---》 digestReqest js(内部封装各类设备操作接口) --- 调用 ---》digest-auth-client.js (封装digest认证过程工具)

具体代码内容:

digest-auth-client.js

const http = require('http')
const crypto = require('crypto')

const md5 = (data) => {
  const md5 = crypto.createHash('md5')
  return md5.update(data).digest('hex')
}

export const digestRequest = (options, data, user, pass, cb) => {
  return new Promise(function(resolve, reject) {
    const req = http.request(options, (res) => {
      resolve(res)
    })

    req.on('error', (e) => {
      reject(e)
    })

    if (data) {
      req.write(data)
    }
    req.end()
  }).then(function(res) {
    console.log(`STATUS: ${res.statusCode}`)
    console.log(`HEADERS: ${JSON.stringify(res.headers)}`)

    if (res.statusCode === 401) {
      const cnonce = md5(String(new Date().getTime()))
      const auth = res.headers['www-authenticate']
      let realm, nonce, qop
      const authSplit = auth.split(',')

      for (const k in authSplit) {
        if (authSplit[k].indexOf('realm=') >= 0) {
          const realmSplit = authSplit[k].split('="')
          realm = realmSplit[realmSplit.length - 1]
          realm = realm.substring(0, realm.length - 1)
        }

        if (authSplit[k].indexOf('nonce=') >= 0) {
          const nonceSplit = authSplit[k].split('="')
          nonce = nonceSplit[nonceSplit.length - 1]
          nonce = nonce.substring(0, nonce.length - 1)
        }

        if (authSplit[k].indexOf('qop=') >= 0) {
          const qopSplit = authSplit[k].split('="')
          qop = qopSplit[qopSplit.length - 1]
          qop = qop.substring(0, qop.length - 1)
        }
      }

      const HA1 = md5(user + ':' + realm + ':' + pass)
      const HA2 = md5(options.method + ':' + options.path)
      const response = md5(HA1 + ':' + nonce + ':00000001:' + cnonce + ':' + qop + ':' + HA2)
      options.headers.Authorization = 'Digest username="' + user + '",realm="' + realm + '",nonce="' + nonce + '",uri="' + options.path + '",cnonce="' + cnonce + '",nc=00000001,algorithm=MD5,response="' + response + '",qop="' + qop + '"'
      return request(options, data)
    } else {
      return request(options, data)
    }
  })
}

const request = (options, data) => {
  return new Promise(function(resolve, reject) {
    const req = http.request(options, (res) => {
      let resData
      res.setEncoding('utf8')
      res.on('data', (chunk) => {
        resData += chunk
      })
      res.on('end', () => {
        resolve(resData)
      })
    })

    req.on('error', (e) => {
      reject(e)
    })

    if (data) {
      req.write(data)
    }

    req.end()
  })
}

digestReqest js:

import { digestRequest } from './node-digest-auth-client'


// 默认的请求配置参数信息
const defaultOption = {
  hostname: '127.0.0.1',
  port: 80,
  path: '/ISAPI/Security/userCheck',
  method: 'GET',
  headers: {
    'Connection': 'Keep-Alive',
    'Content-Type': 'application/json; charset="utf-8"'
  }
}

export const ISApiRequest = function(option, data, username, password) {
  // 将用户的请求参数信息与默认的请求参数信息进行合并
  const reqOption = Object.assign(defaultOption, option)
  return digestRequest(
    reqOption,
    data,
    username,
    password
  )
}

业务代码: 

// vue组件的method方法: 
handleGetDeviceAbility: function() {
      ISApiRequest(
        {
          hostname: '10.41.102.136',
          port: 80,
          path: '/ISAPI/System/capabilities',
          method: 'GET'
        },
        null, 'admin', 'HKJFxmzcb@2018',
        (err, data) => {
          console.log(err)
          console.log(data)
        }
      ).then(resp => {
        console.log('request response data is')
        console.log(resp)
      })
    }

最终效果

 

优化思考: 

1、 http库截止目前貌似已经不推荐使用了, 可以将http用needle进行调换,需要对部分接口做些调整;(已做优化, 优化文章地址见 https://blog.csdn.net/Harvay/article/details/112020107

2、 响应数据目前有json以及xml格式,可以根据response header头信息进行判断后解析数据。

 

分享以上,供参考

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值