该问题涉及在本地全栈(在本例中为 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
有两个子域:
app.mylocaldomain.com
它通过 Webpack Dev Server 托管 React 应用程序api.mylocaldomain.com
它通过 Node.js + Express 托管 FE API 服务器
需要
能够从 向 发出 HTTPS AJAXhttps://app.mylocaldomain.com
请求https://api.mylocaldomain.com/some-route
脚步
添加
app.mylocaldomain.com
和app.mylocaldomain.com
到/etc/hosts
... 127.0.0.1 api.mylocaldomain.com 127.0.0.1 app.mylocaldomain.com ...
创建
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
创建
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
创建应用程序
cert.key
和cert.pem
文件:openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout app/cert.key -out app/cert.pem -config app/req.cnf -sha256
创建API
cert.key
和cert.pem
文件:openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout api/cert.key -out api/cert.pem -config api/req.cnf -sha256
将两个证书都加载到 Mac Keychain Access 并批准两者都受信任
这是钥匙串中批准的两个证书的工作结果:
配置应用程序 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') }, ... }
sudo yarn start
使用(在 npm 脚本中)启动应用程序webpack-dev-server
以允许使用443
端口这是工作结果:
通过 HTTPS 正确加载应用程序的位置。
请注意,省略
sudo
会导致以下情况(或类似情况,具体取决于您的 Webpack Dev Server 设置):... Error: listen EACCES: permission denied 127.0.0.1:443 ...
将 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); } }); ...
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
取决于各种设置排列。