Node标准https和node-fetch在提供证书时的区别

cra*_*aig 6 javascript client-certificates node.js node-https node-fetch

ADP 的 REST API 要求每次请求时都发送 SSL 证书和私钥。

当我使用“标准 Node.js HTTP(S) 模块”时:

require('dotenv').config()

const fs = require('fs')
const path = require('path')

const certificate_path = path.resolve('../credentials/certificate.pem')
const private_key_path = path.resolve('../credentials/private.key')

const options = {
    hostname: 'api.adp.com',
    path: '/hr/v2/workers/ABCDEFGHIJKLMNOP',
    method: 'GET',
    headers: {
        'Accept': 'application/json;masked=false',
        'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`
    },
    cert: fs.readFileSync(certificate_path, "utf8"),
    key: fs.readFileSync(private_key_path, "utf8"),
};

require('https').get(options, res => {

  let data = [];

  res.on('data', chunk => {
    data.push(chunk);
  });

  res.on('end', () => {

    const workers = JSON.parse(Buffer.concat(data).toString());

    for(worker of workers.workers) {
      console.log(`Got worker with id: ${worker.associateOID}, name: ${worker.person.legalName.formattedName}`);
    }

  });

}).on('error', err => {
  console.log('Error: ', err.message);
});

Run Code Online (Sandbox Code Playgroud)

该请求按预期工作:

$ node ./standard.js
Got worker with id: ABCDEFGHIJKLMNOP, name: Last, First
Run Code Online (Sandbox Code Playgroud)

但是,当我使用节点获取时:

require('dotenv').config()

const fs = require('fs')
const path = require('path')

const certificate_path = path.resolve('../credentials/certificate.pem')
const private_key_path = path.resolve('../credentials/private.key')

const url = 'https://accounts.adp.com/hr/v2/workers/ABCDEFGHIJKLMNOP'

const options = {
  headers: {
    'Accept': 'application/json;masked=false',
    'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`
  },
  agent: new require('https').Agent({
      cert: fs.readFileSync(certificate_path, "utf8"),
      key: fs.readFileSync(private_key_path, "utf8")
  })
}

fetch(url,options)
  .then((response) => response.json())
  .then((body) => {
      console.log(body);
  });
Run Code Online (Sandbox Code Playgroud)

我收到错误:

$ node ./fetch.js   
{
  response: {
    responseCode: 401,
    methodCode: 'GET',
    resourceUri: { href: '/hr/v2/workers/ABCDEFGHIJKLMNOP' },
    serverRequestDateTime: '2023-03-30T14:25:23.351Z',
    applicationCode: {
      code: 401,
      typeCode: 'error',
      message: 'Request did not provide the required two-way TLS certificate'
    },
    client_ip_adddress: 'a.b.c.d',
    'adp-correlationID': '61f76d29-04e1-48b8-be9d-acf459408b2b'
  }
}
Run Code Online (Sandbox Code Playgroud)

我在第二种方法中缺少什么?

jos*_*736 5

也许令人惊讶的是,节点的内置fetch()全局不使用传统内置http/https模块提供的HTTP堆栈。

相反,它使用并行的、从头开始的 HTTP 堆栈重写,称为undici

鉴于 的fetch()HTTP 堆栈完全独立于标准 HTTP 堆栈,因此您可以提供给 et al 的选项http.get不能与 一起使用也就不足为奇了fetch()

查看文档,您似乎可以传入自定义Dispatcher对象,该对象又可以自定义Client用于连接到服务器的对象。可以Client配置请求中使用的TLS 客户端证书。

不幸的是,此时,node 捆绑的 undici 并未暴露给用户代码;你做不到require()。我没有深入研究内部结构,但您可能必须在项目中安装undici 包,才能访问构建Dispatcher可以提供客户端证书的自定义所需的类。

但请注意,undici 的版本是与 Node 静态捆绑的,因此它将取决于安装的 Node 发行版本。我可以想象由于 undici 的内置版本和打包版本的重复和/或不匹配版本而导致的奇怪错误。当事情被认为稳定时,节点的未来版本可能会公开捆绑的undici。

鉴于上述情况,至少在短期内,我个人会坚持使用传统的http( s) 模块而不是fetch().


Joe*_*eer 1

我没有看到任何 node-fetch 的导入,因此我假设您正在使用添加到 Node18 的新本机 fetch。新的全局获取(尚)不支持代理选项。

了解为什么代理选项在节点本机获取中不可用?