目前,大多数Web开发工作都在 http://localhost:port下完成。然而,本地开发使用https有时是必要的,例如向一个https站点传送数据

本文讨论了如何在本地开发环境开启https

https自签名证书

https是http协议+ssl协议,为网络请求中的数据提供了加密保护,减少数据被截获的风险。没有特殊原因的话,生产环境的服务都需要开启https

正常情况下,为部署在生产环境的http服务开启https需要几个条件:

  1. 服务绑定在域名上,且该域名通过DNS解析到了服务所在主机的ip。如果是国内域名,还需要给域名备案
  2. 域名绑定了数字证书,该证书来自受信任的证书颁发机构(CA),如Let's Encrypt
  3. 服务本身支持https功能,已经开启,未被防火墙阻拦

在本地开发环境,localhost这个域名被系统默认解析到了 127.0.0.1,我们可以直接用

至于数字证书,mkcert可以创建只在本地受信任的https自签名证书,非常适合开发测试

WSL下安装 mkcert并初始化

sudo apt install mkcert
mkcert --install

~/.local/share/mkcert下的 rootCA.pem就是mkcert的根证书,它会告诉WSL:以后看到由我 mkcert生成的证书,就信任它别报警哈

由于Windows和WSL的证书是分开管理的,还需在Windows下导入 mkcert根证书

win+R打开运行窗口,输入 certmgr.msc,打开证书管理器,在受信任的根证书颁发机构-证书上点击右键,所有任务-导入,添加WSL中的 ~/.local/share/mkcert/rootCA.pem。添加时有警告

image.png|500

前端开启https

如果你使用netx.js搭建前端且版本在 13.5.1以上,按照官方教程packages.json中增加 dev-https快捷命令

"dev-https": "next dev --experimental-https",

然后启动

pnpm run dev-https

该步骤会自动调用 mkcert(如果未安装会自动下载安装),为域名 localhost创建https证书,保存到 certificates目录下,并开启https服务

如果你使用其他框架,在前端项目目录下运行

mkcert localhost

这会生成 localhost.pem证书文件和 localhost-key.pem证书密钥文件(和next.js框架下 certificates目录内的一样),然后根据框架文档,一般都需要手动引入这两个文件,然后开启https

假定前端服务在 https://localhost:3000,如果你还没在Windows导入 mkcert的根证书,或导入后没重启过浏览器,就会有这样的报警

image.png|550

每一步都完整操作的话,此时Windows的浏览器应该已经可以访问https前端了

后端开启https

如果前端已经开启https,而后端仍是http,前端将无法访问后端的任何接口,这是浏览器同源策略所限制的,即不能从安全源跨域访问不安全源。如果后端开启了https,而前端未开启,从前端仍然可以访问后端——浏览器不会限制从不安全源跨域访问安全源

为了让前后端互相访问,需要也为后端开启https。在后端项目目录下运行 mkcert localhostlocalhost域名生成证书

根据后端框架的不同,使证书生效、开启https服务的方式也不同

如果你使用fastapi,首先设置好跨域策略中间件

from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins="localhost",
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

allow_origins可以只写允许跨域访问后端接口的域名 localhost,或域名+端口 localhost:3000,或协议+域名+端口 https://localhost:3000

然后在启动后端服务时,指定 --ssl-keyfile--ssl-certfile

uvicorn src.abqus.main:app --reload --ssl-keyfile localhost-key.pem --ssl-certfile localhost.pem

这时,https://localhost:8000后端服务、https://localhost:3000前端服务都已经开启,且两者可以互相访问

如果前端或后端框架本身不支持https服务、或配置https比较麻烦,还可以考虑在本地开一个nginx服务器,通过反向代理将https请求传递到http服务

在本地使用localhost以外的域名

如果你不想用 localhost,想换个域名,就得把自定义的域名解析到 127.0.0.1本地主机。hosts文件允许我们这样做

WSL的hosts文件在 /etc/hosts,Windows的hosts文件在 C:\Windows\System32\drivers\etc\hosts,在这两个文件添加以下内容

127.0.0.1       dev.local

重启浏览器后,自定义的域名解析就完成了

接着还需要配置前端、后端服务,指定域名。不同框架的配置也不同,对于next.js,只需为快捷命令添加 -H参数。next.js会自动重新为 dev.local生成自签名证书,文件名还是 localhost.pem,但内部的编码已经改变

"dev-https": "next dev --experimental-https -H dev.local",

对于fastapi,则需要重新为 dev.local生成自签名证书,启动服务时指定 host参数为 dev.local

mkcert dev.local
uvicorn src.abqus.main:app --reload --ssl-keyfile dev.local-key.pem --ssl-certfile dev.local.pem --host dev.local

至此,我们就可以通过 https://dev.local:3000https://dev.local:8000访问前端和后端

如果你觉得写端口还是太麻烦,希望为前端和后端分配两个域名,比如 dev.frontdev.back,就需要使用nginx等反向代理服务器

不写端口不代表没有端口,而是使用默认端口(https的80和https的443)。一台主机不能在443端口开两个服务,所以只能将 localhost:3000转发到 dev.front:443,将 localhost:8000转发到 dev.back:443

反向代理的配置,本文不再详述

结语

经过以上配置,你应该能在本地使用https开发了。开发环境与生产环境的差别进一步减少,能提前规避很多问题

玩得开心!