webpack-dev-server + nginx + node调试https微信授权网页

4,007 阅读4分钟

概述

一般情况下,在使用webpack-dev-server热更新的时候,使用的域名都是localhost/127.0.0.1之类的,都是基于http协议的。

基于nodejs对webpack入口的处理如下:

webpackConfig.entry[entryKey] = [
  // 这里会发送一次请求
  'webpack-dev-server/client?http://0.0.0.0:8080',
  'webpack/hot/dev-server',
  ...webpackConfig.entry[entryKey]
];

热更新成功后,浏览器会以当前域名+端口发送一个用于热更新的请求。该请求默认域名是webpack入口配置中webpack-dev-server/client?http://0.0.0.0:8080中问号后面的参数,如果参数中主机名是0.0.0.0,将会被替换成self.location.hostname/sockjs-node请求被webpack-dev-server服务器处理。

具体的替换规则有多种情况,可以参考webpack-dev-server中createSocketUrl的源码。

nginx + host 配置url

对于需要公众号微信授权的网页,每个公众号都可以配置两个js安全回调域名。所以网页的域名必须是公众号中配置存在的。比如原始网页访问地址是http://127.0.0.1:8080/test/abc。公众号配置的安全回调域名是abc.com。如果该网页需要经过微信授权(授权过程不表),那么需要通过配置让网页的域名变成abc.com。访问地址就是abc.com/test/abc。否则微信会报redirect_uri参数错误

通过host配置以及nginx端口转发后,就可以通过abc.com/test/abc访问http://127.0.0.1:8080/test/abc了。配置如下:

  1. host配置,将abc.com代理到本地地址
127.0.0.1 abc.com

只配置host的话可以通过abc.com:8080/test/abc来访问网页。这时候热更新请求的地址是http://abc.com:8080/sockjs-node/info

  1. nginx配置
server {
  listen       80;
  listen       443 ssl;

  server_name abc.com;


  ssl_certificate    /usr/local/etc/nginx/abc.crt;
  ssl_certificate_key  /usr/local/etc/nginx/abc.key;
  # 省略一些配置
  
  # 3200是node端口
  location / {
    proxy_pass   http://127.0.0.1:3200/; 
    # 其他配置
  }

  # 8080 是前端资源端口
  location /test {
    proxy_pass   http://127.0.0.1:8080/test;
    # 其他配置
  }
}

完成这两步,就可以通过http://abc.com/test/abc来访问网页了,并且可以通过微信授权。但是仍然是在http的协议下。在这种情况下,热更新请求的地址仍然是http://abc.com:8080/sockjs-node/info,显然nginx没有监听8080端口以及/sockjs-node,因为8080端口已经被webpack-dev-server占用了。但是该请求仍是可以正常打开的,不经过nginx也可以,请求的就是本地资源,该请求被webpack-dev-server服务器处理了。。

在某些情况下,需要https访问,比如service-worker,微信支付(支付目录是https协议的)等。

https协议热更新

首先在nginx中配置https://abc.com的证书,并监听443端口。上述代码中已经配置。打开https://abc.com/test/abc连接,页面可以打开。热更新请求的地址是https://abc.com:8080/sockjs-node/info,可以看到仍然是8080端口。因为根据createSocketUrl的源码,在入口中配置了webpack-dev-server/client?http://0.0.0.0:8080的情况下,如果该链接query参数中有端口并且webpack-dev-server没有配置端口的情况下,就使用该端口。

这时候情况就不一样了,打不开热更新请求的地址。8080没有经过nginx,该请求请求的仍然是webpack-dev-server服务器资源。但是webpack-dev-server服务器没有配置https证书,导致无法访问到。

关键点来了。如果该请求经过nginx,再代理到本地webpack-dev-server服务器就好了。因为nginx中配置了https证书。

查了好长时间,看到一个相关issue,在webpack-dev-server5月份发布的3.4.0 (2019-05-17)release版本中,提供了sockPort,sockHost选项,用于配置热更新请求的端口和主机,官网文档中可以查到。

所以改造需要三步。

  1. 入口改造
webpackConfig.entry[entryKey] = [
  // 这里会发送一次请求
  'webpack-dev-server/client?http://0.0.0.0:8080',
  'webpack/hot/dev-server',
  ...webpackConfig.entry[entryKey]
];

变更为

webpackConfig.entry[entryKey] = [
  // 这里会发送一次请求
  'webpack-dev-server/client',
  'webpack/hot/dev-server',
  ...webpackConfig.entry[entryKey]
];

因为不要读取这里的配置了。

  1. webpack-dev-server添加该配置
sockPort: 443
  1. nginx转发/sockjs-node
location /sockjs-node {
  proxy_set_header X-Real-IP  $remote_addr;
  proxy_set_header X-Forwarded-For $remote_addr;
  proxy_set_header Host $host;
  proxy_pass http://127.0.0.1:8080;
  proxy_redirect off;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";  
}

简单来说,通过配置sockPort后,热更新请求变成了https://abc.com/sockjs-node/info,443可以省略。443端口被nginx转发到本地http协议的8080端口,最终请求的是webpack-dev-server服务器。如图:

这里几乎同时发了两个请求。经过测试,可以改为只发一个。在上面的配置中去掉webpack-dev-server/client这一行,并且webpack-dev-server添加配置sockHost: 'abc.com'即可。