为什么本地开发要使用 HTTPS?
因为有很多 Web API 必须在 HTTPS 环境下才可以使用。比如,Clipboard API and events(www.w3.org/TR/clipboar…)中的navigator.clipboard
对象是通过扩展Navigator
接口定义的:
partial interface Navigator {
[SecureContext, SameObject] readonly attribute Clipboard clipboard;
};
在此,partial
的意思就是扩展Navigator
接口,给它增加一个Clipboard
类型的只读成员属性clipboard
。[SecureContext, SameObject]
中的两个关键字是 “扩展属性”(extended attribute),在这里修饰接口成员clipboard
。SecureContext
表示 `navigator
只能在 “安全上下文” 中暴露clipboard
属性,SameObject
表示每次访问navigator.clipboard
必须都返回相同的值。(参见:heycam.github.io/webidl/#Sec…://heycam.github.io/webidl/#SameObject。)
关于 “安全上下文”,W3C 的 Secure Contexts 文档(w3c.github.io/webappsec-s…)中有详细解释。根据 MDN,全站 HTTPS 和通过 http://localhost 交付的网页是安全的。可以通过window.isSecureContext
属性来检测当前上下文是否安全。
if (window.isSecureContext) {
navigator.clipboard.writeText('Write this to clipboard!').then(ok => true, err => false)
}
另外还有一个重要原因,就是有时候 HTTP 的本地请求可能会被 HTTPS 服务器拒绝。
无论如何,我们在开发实践中都有可能碰到将本地 Web 服务 HTTPS 化的需求。这时候,我们可以创建自己私钥并签名一个根证书,并在开发环境中配置安装和信任自己的根证书。然后再通过这个根证书和私钥签发相应域名的 SSL 证书。
好吧,开始干吧。
第一步:生成自签名的根 SSL 证书
首先生成一个 RSA-2048 加密的私钥,保存为 localCA.key。生成过程中,会提示输入密码(pass phrase),以后在使用生成的私钥签发证书时都要输入这个密码。
openssl genrsa -des3 -out localCA.key 2048
接下来用这个私钥生成一个根 SSL 证书,保存为 localCA.pem。有效期为 1825 天(5 年)。
openssl req -x509 -new -nodes -key localCA.key -sha256 -days 1825 -out localCA.pem
在此期间,会提示输入:
- Country Name(2 字母):国家 / 地区
- State or Province Name(全称):省 / 市 / 自治区
- Locality Name:所在地
- Organization Name:组织单位
- Organizational Unit Name:部门
- Common Name:常用名称,根证书的名称
第二步:配置本地环境信任自签名根证书
打开 macOS 中的 “钥匙串访问” 应用:
-
“文件> 导入项目”,选择 localCA.pem,打开(需要输入密码)
-
双击导入的证书,在 “信任” 中选择“始终信任”
-
退出当前窗口(需要输入密码),“此证书已标记为受所有用户信任”
第三步:签发域名 SSL 证书
首先,创建域名 SSL 证书的私钥,“ext” 的意思在这里表示 “泛域名”,可以随意命名
openssl genrsa -out ext.yourdomain.com.key 2048
然后,用这个密钥创建一个 CSR(Certificate Signing Request,证书签名请求)文件
openssl req -new -key ext.yourdomain.com.key -out ext.yourdomain.com.csr
在创建证书之前,还要创建一个配置文件,将其命名为 ext.yourdomain.com.ext,包含如下内容:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.yourdomain.com
注意这里的
*.yourdomain.com
,表示 SSL 证书可用于泛域名。
最后,为*.yourdomain.com
域名签发 SSL 证书
openssl x509 -req -in ext.yourdomain.com.csr \
-CA localCA.pem -CAkey localCA.key \
-CAcreateserial -out ext.yourdomain.com.crt \
-days 1825 -sha256 -extfile ext.yourdomain.com.ext
把生成的证书和私钥文件复制到相应目录
- ext.yourdomain.com.crt
- ext.yourdomain.com.key
假设使用 Webpack 的 devServer,配置如下:
devServer: {
proxy: {
'*': 'http://127.0.0.1:9360'
},
port: 443,
https: {
key: fs.readFileSync(path.join(__dirname, 'ext.yourdomain.com.key')),
cert: fs.readFileSync(path.join(__dirname, 'ext.yourdomain.com.crt'))
},
host: 'dev.yourdomain.com',
allowedHosts: ['dev.yourdomain.com']
watchContentBase: true,
// hot: true
},
配置 HOST 文件:
127.0.0.1 dev.yourdomain.com
127.0.0.1 test.yourdomain.com
重启服务:
再换一个子域名:
注意,
*.yourdomain.com
只限一级泛子域名,即dev.yourdomain.com
或test.yourdomain.com
是可以的,而dev.h5.yourdomain.com
则不可以!(如果有二级泛子域名需求,可以单独生成比如*.h5.yourdomain.com
这样证书。)