首页 > 解决方案 > 在本地 Node.js/Express/React 开发中使用自签名证书

问题描述

该问题涉及在本地全栈(在本例中为 React 应用程序和 Node.js FE 服务器)开发中使用自签名证书。

请注意,问题本身可能并不像它所暗示的那么复杂。简单地说,我需要描述整个设置以避免处理不相关的琐碎建议。此外,当/如果问题得到回答时,它应该作为这种努力的一个很好的起点。

原因

许多功能,包括但不限于共享签名 cookie、http2 等...,只能通过 HTTPS 工作或需要兄弟/父域设置,这就需要创建证书并同时运行 Webpack Dev Server 和 Nodejs FE 服务器通过 HTTPS(本地)在显式域上而不是在localhost.

请注意,该问题特定于本地开发,因为它在生产中使用非自签名证书和 HTTPS 正确配置。

假设

虽然可以使用 Let's Encrypt 之类的东西创建“普通”证书,但并不总是可行或可取的(公司政策或类似政策)。

因此,出于纯粹的学术目的,我们选择使用自签名证书

设置

操作系统:Mac Catalina 10.15.2 节点:13.8

有两个子域:

需要

能够从 向 发出 HTTPS AJAXhttps://app.mylocaldomain.com请求https://api.mylocaldomain.com/some-route

脚步

  1. 添加app.mylocaldomain.comapp.mylocaldomain.com/etc/hosts

    ...
    127.0.0.1   api.mylocaldomain.com
    127.0.0.1   app.mylocaldomain.com
    ...
    
  2. 创建api/req.cnf文件

    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    C = US
    ST = State
    L = Location
    O = myorg
    OU = myunit 
    CN = api.mylocaldomain.com
    [v3_req]
    keyUsage = critical, digitalSignature, keyAgreement
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = api.mylocaldomain.com
    
  3. 创建app/req.cnf文件

    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    C = US
    ST = State
    L = Location
    O = myorg
    OU = myunit 
    CN = app.mylocaldomain.com
    [v3_req]
    keyUsage = critical, digitalSignature, keyAgreement
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = app.mylocaldomain.com
    
  4. 创建应用程序 cert.keycert.pem文件:

    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout app/cert.key -out app/cert.pem -config app/req.cnf -sha256
    
  5. 创建API cert.keycert.pem文件:

    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout api/cert.key -out api/cert.pem -config api/req.cnf -sha256
    
  6. 将两个证书都加载到 Mac Keychain Access 并批准两者都受信任

    这是钥匙串中批准的两个证书的工作结果:

    在此处输入图像描述

  7. 配置应用程序 Webpack Dev Server 以通过 HTTPS 启动

    devServer: {
        ...
        host: 'app.mylocaldomain.com',
        port: 443,
        https: {
            key: FS.readFileSync('/path/to/app/cert.key'),
            cert: FS.readFileSync('/path/to/app/cert.pem')
        },
        ...
    }
    
  8. sudo yarn start使用(在 npm 脚本中)启动应用程序webpack-dev-server以允许使用443端口

    这是工作结果:

    在此处输入图像描述

    通过 HTTPS 正确加载应用程序的位置。

    请注意,省略sudo会导致以下情况(或类似情况,具体取决于您的 Webpack Dev Server 设置):

    ...
    Error: listen EACCES: permission denied 127.0.0.1:443
    ...
    
  9. 将 FE 服务器配置为通过 HTTPS 启动

    ...
    const app = express(...)
    ...
    const key = fs.readFileSync('/path/to/api/cert.key');
    const cert = fs.readFileSync('/path/to/api/cert.pem');
    
    https.createServer({key, cert}, app).listen(PORT, error => {
        if (!error) {
            console.info(`Listening on port: ${PORT}`);
        } else {
            console.error(error);
        }
    });
    ...
    
  10. sudo yarn start --open --public api.mylocaldomain.com --port 443使用(node lib/在 npm 脚本中)启动 FE API 服务器

    yarn run v1.22.0
    $ node lib/ --public api.mylocaldomain.com
    Listening on port: 5000   
    

问题

上面的设置应该已经足够了,但是,调用 AJAX 请求https://api.mylocaldomain.com/status(是的,路由存在)会在 Chrome 控制台中导致以下(为清楚起见而删节)错误:

GET https://api.mylocaldomain.com/status
net::ERR_CERT_COMMON_NAME_INVALID

如果您https://api.mylocaldomain.com/status直接在 Chrome 中访问,您将获得以下信息:

在此处输入图像描述

这表明 App 和 API 的证书以某种方式混合在一起,但我多次以各种排列方式运行上述步骤,但看不到会发生这种情况。也许/etc/hosts映射搞砸了?这是有道理的,因为两者app.api.都被映射到127.0.0.1创建混合中。但是,我对如何解决这个问题感到矛盾。

事实上,如果经过检查,Chrome 似乎(至少)在访问 API 时会加载 App 证书。为什么?

按照上面的链接会导致:

在此处输入图像描述

调用curl -XGET https://api.mylocaldomain.com/status会导致以下错误:

>>> curl -XGET https://api.mylocaldomain.com/status
curl: (60) SSL: no alternative certificate subject name matches target host name 'api.mylocaldomain.com'
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

这很奇怪,正如上面 APIapi.mylocaldomain.com中明确定义的那样。req.cnf


建议?

我认为这是一件极其微不足道的事情,我会因为错过它而感到尴尬,但经过多次通过和重试后,它可能已经进入了我的盲点。

附录 A。

创建多域*.mylocaldomain.com证书也不起作用,错误是“无效的公用名”或Invalid Host header取决于各种设置排列。

标签: javascriptnode.jswebpackssl-certificateself-signed-certificate

解决方案


推荐阅读