Node搭建HTTPS服务器

375 阅读2分钟

关于 HTTPS 协议的文章已经有很多,其原理和与 HTTP 协议的区别大家也都知道了,所以本文着重于介绍 HTTPS 服务器的搭建和相互通信的方法的介绍。

本文使用 Node 搭建服务器。

HTTP 服务器

我们首先搭 HTTP 服务器:

import { createServer } from 'http'

createServer((req, res) => {
  res.end('http-a')
}).listen(9000)

用 HTTP 协议可以直接访问。

HTTPS 服务器

证书

众所周知,HTTPS 服务器需要证书。

我们使用 OpenSSL 生成自签名证书:

openssl genrsa -out key.pem
openssl req -new -key key.pem -out csr.pem
openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out cert.pem

genrsa命令用于生成私钥。

req命令用于生成符合证书要求并包含私钥信息的文件。使用时会要求输入参数。

在这一步中,为了接下来访问时的域名统一,我们将参数 Common Name (CN) 设置为 localhost,其余默认。

x509命令用于生成公钥,-days参数表示有效天数。

需要注意的是,生成文件的后缀名并不关键,比如有些人习惯生成 .key,.csr,.crt 文件。

服务器

创建 HTTPS 服务器时需要读取证书和私钥文件。

import { readFileSync } from 'fs'
import { createServer } from 'https'

createServer(
  {
    key: readFileSync('./certificate/key.pem'),
    cert: readFileSync('./certificate/cert.pem')
  },
  (req, res) => {
    res.end('HTTPS')
  }
).listen(9001)

其它格式的证书

https 包也可以使用 pfx 格式的证书。

{
  pfx: readFileSync('.pfx'),
  passphrase: 'password',
}

访问

如果使用 Postman 访问 https://127.0.0.1:9001,可以成功。但如果我们用 Chrome 访问,会提示“您的连接不是私密连接”,这是 Chrome 对自签名服务器的限制,我们需要点击“高级”才能继续访问。

在 Node 程序中,如果我们用 http 和 https 包的 get 方法访问:

import Http from 'http'
import Https from 'https'

async function request(url) {
  let method = /^https/.test(url) ? Https.get : Http.get

  return new Promise((resolve, reject) => {
    method(url, res => {
      if (res.statusCode === 200) {
        let data = ''
        res.setEncoding('utf-8')
        res.on('data', payload => (data += payload))
        res.on('end', () => {
          resolve(data)
        })
      } else {
        reject(new Error(res.statusCode))
      }
    }).on('error', er => reject(er))
  })
}

console.debug(await request('http://localhost:9000'))
console.debug(await request('https://localhost:9001'))

访问 http://127.0.0.1:9000 时没有问题,但访问 https://127.0.0.1:9001,会报错提示 “RequestError: self signed certificate”,同样也是自签名证书的问题。

自签名证书

有以下几种办法忽略“自签名证书”的问题。

  1. 设置全局配置
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'

或者

Https.globalAgent.options.rejectUnauthorized = false

这种方法有点危险,因为全局的 https 请求都不会验证证书。

  1. 设置 Http.get 方法的参数
Http.get(url,{
  rejectUnauthorized: false
},callback)
  1. 设置公钥

在发出请求时,设置于服务器使用的相同公钥。

Http.get(url,{
  ca: readFileSync('./certificate/cert.pem')
},callback)

权威签名证书

如果想规避自签名证书的缺点,特别是在外网环境中,那么可以从权威机构申请证书,放到服务器中。