需求说明

客户平台需要访问注册到omc的网关设备web。

网关设备注册到omc后,建立了一个tcp连接。omc通过这个连接直接调用网关设备的api来进行数据交互,因此访问omc的ip地址就相当于可以直接访问到网关设备。

例:访问下面这个ip地址,就相当于通过omc直接访问到了网关设备的web,提供web的是omc,数据呈现的方式和网关设备的web一样,可以认为是直接访问到了网关设备。


但是客户平台和omc部署在不同服务器上,客户平台web跳转到omc存在跨域的问题,所以跳转时需要填omc的账号密码。理论上密码不应该进行传输,但是如果不传输,omc前端和后端交互时不知道客户平台的账号密码,当前omc的前端架构如果不知道账号密码,是不能进行跳转的,因此需要设计一套鉴权方案。


解决方案

使用数字签名验证客户身份。

原理

数字签名

      • 定义:数字签名是一种通过加密技术确保信息的完整性和来源的机制。它允许接收方验证发送方的身份,并确认消息在传输过程中未被篡改。

      • 工作原理

        1. 客户端使用其私钥(API 密钥的秘密部分)对请求数据(例如,时间戳和请求体)生成 HMAC 签名。
        2. 这个签名与请求一起发送到服务端。
        3. 服务端使用存储的私钥(API 密钥的秘密部分)和相同的请求数据重新生成签名。
        4. 服务端比较生成的签名与客户端发送的签名。如果两者匹配,则请求有效。

实施细节

服务端生成密钥对,将客户端的公钥apiKey和私钥secretKey存放到mongoDB,并将密钥对给客户平台。omc不能明文储存私钥,需进行对称加密。

客户平台

    • 访问omc鉴权接口,获取omc返回的token

请求头携带API信息ACCESS-KEY ACCESS-SIGN ACCESS-TIMESTAMP

ACCESS-SIGN的请求头是对timestamp + method + requestPath + body字符串(+表示字符串连接),以及SecretKey,使用HMAC SHA256方法加密,通过Base-64编码输出而得到的。

如:sign=CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(timestamp + 'GET' + '/api/login', SecretKey))

其中,timestamp的值与ACCESS-TIMESTAMP请求头相同,为ISO格式,如2020-12-08T09:08:57.715Z。

method是请求方法,字母全部大写:GET/POST。

requestPath是请求接口路径。如:/api/login

body是指请求主体的字符串,如果请求没有主体(通常为GET请求)则body可省略。

http://192.168.1.28/api/login


    • 访问omc登录接口,登录成功


http://192.168.1.28/platformIndex.html?mac=00:53:4c:40:1a:50&token=U2FsdGVkX19ZgrRfjg6xXP9SNpctIWJPulBFjPFPltI=%3D 


omc前端

    • 响应客户平台登录请求

收到客户平台跳转请求时,访问后端接口验证token有效性,若token有效服务端返回客户账户密码。

 http://192.168.1.28/jscmd/login

omc后端

    • 响应客户平台鉴权请求,返回token
  • 响应'/api/login' ,根据公钥ACCESS-KEY,从数据库查询私钥,计算ACCESS-SIGN,若与请求一致,返回token。
    • 响应omc前端登录请求,登录成功

 响应'/jscmd/login' ,登录成功。

    •  后端计算ACCESS-SIGN代码参考
std::string base64Encode(const unsigned char* data, size_t length) {
  BIO *bmem, *b64;
  BUF_MEM* bptr;
  b64 = BIO_new(BIO_f_base64());
  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
  bmem = BIO_new(BIO_s_mem());
  b64 = BIO_push(b64, bmem);
  BIO_write(b64, data, length);
  BIO_flush(b64);
  BIO_get_mem_ptr(b64, &bptr);

  std::string encoded(bptr->data, bptr->length);
  BIO_free_all(b64);

  return encoded;
}

std::string createSignature(const std::string& secretKey,
                            const std::string& timestamp,
                            const std::string& method,
                            const std::string& endpoint,
                            const std::string& body) {
  std::string data = timestamp + method + endpoint + body;

  unsigned char digest[EVP_MAX_MD_SIZE];
  unsigned int digestLength;

  HMAC(EVP_sha256(), secretKey.c_str(), secretKey.length(),
       reinterpret_cast<const unsigned char*>(data.c_str()), data.length(),
       digest, &digestLength);

  std::string signature = base64Encode(digest, digestLength);

  return signature;
}


流程图

  • 无标签
写评论...