本地开发代理更改origin和响应cookie(webpack)

问题场景

  • 后端api服务开启了跨域校验
  • 并且登录凭据由接口响应设置到cookie(并且为HTTP-ONLY类型),cookie只对线上环境的域名生效

如果前端开发时使用的是localhost或者本地回环地址,前端开发服务简单的去代理接口请求是不行的,因为服务端的set-cookie响应头设置cookie的domain是正常域名(www.somedomain.com)。

结果就是localhost发出的请求无法携带上www.somedomain.com的cookie,也就无法正常登录和调用接口。

解决方案

参见webapck文档:开发服务器使用功能强大的 http-proxy-middleware 软件包。 查看其 documentation 了解更多高级用法。

主要是2个关键点:

  1. 请求转发时,要替换header里的origin、host、referer
  2. 响应转发时,要把cookie的domain配置改成localhost或则本地回环地址,webpack使用的是cookieDomainRewrite配置。

vue.config.js配置(基于webpack)

// 【可修改】本地开发-代理接口的目标的 ip 地址url(可按需修改,如http://192.168.100.11、http://192.168.100.43)
const API_SERVER_IP_URL = 'http://192.168.100.11';
 
// 【以下内容看不懂不要改】
 
// 本地开发用的域名
const LOCAL_DEV_HOST = 'localhost';
 
// 用于替换请求头里的host和origin:
const REQUEST_HEADER_HOST = 'target.domain.com';
const REQUEST_HEADER_ORIGIN = `http://${REQUEST_HEADER_HOST}`;
/**
 * 代理请求时,替换请求头里的host和origin
 * - 后端接口只认生产域名,如果host、origin不是生产域名,接口会返回404。
 */
const onProxyReq = function(proxyReq, req, _res) {
  proxyReq.setHeader('host', REQUEST_HEADER_HOST);
  proxyReq.setHeader('origin', REQUEST_HEADER_ORIGIN);
  const { headers } = req;
  const { referer, host } = headers;
  proxyReq.setHeader('referer', referer.replace(new RegExp(host, 'g'), REQUEST_HEADER_HOST));
};
/**
 * 如果接口响应中重定向目标不是本地开发域名,则替换重定向链接的域名为本地开发域名,防止开发到一半跳到线上环境
 */
const onProxyRes = (proxyRes, req, _res) => {
  const { location } = proxyRes.headers;
  if (location) {
    console.log(`检测到接口重定向,重定向目标地址:${location},来源:${req.url}`);
    //
    if (location.startsWith('http://') && !location.startsWith(`http://${LOCAL_DEV_HOST}`)) {
      let url = new URL(location);
      const whiteList = []; // TODO
      if (whiteList.includes(url.hostname)) {
        // 在白名单里,则不改重定向链接的域名
        return;
      }
      url.hostname = LOCAL_DEV_HOST;
      url = url.toString();
      console.log(`-重定向地址已替换:${location} -> ${url}`);
      proxyRes.headers['location'] = url;
    }
  }
};
module.exports = {
  publicPath: '/xxx/',
  devServer: {
    port: 80,
    disableHostCheck: true,
    overlay: {
      warning: true,
      errors: true,
    },
    proxy: {
      '/api': {
        target: API_SERVER_IP_URL,
        changeOrigin: true,
        ws: true,
        // 将接口响应中的set-cookie中的domain修改为本地的host
        cookieDomainRewrite: LOCAL_DEV_HOST,
        onProxyReq,
        onProxyRes,
      },
      '/a': uth{
        target: API_SERVER_IP_URL,
        changeOrigin: true,
        ws: true,
        // 将接口响应中的set-cookie中的domain修改为本地的host
        cookieDomainRewrite: LOCAL_DEV_HOST,
        onProxyReq,
        onProxyRes,
      },
    },
  },
};

webpack.dev.config.js和vue不太一样,可以完整的使用http-proxy-middleware的特性,所以proxy部分可以简写:

...
proxy: [// 可以使用数组配置
      {
        context: [ 'api', 'auth' ], 
        target: API_SERVER_IP_URL,
        changeOrigin: true,
        ws: true,
        // 将接口响应中的set-cookie中的domain修改为本地的host
        cookieDomainRewrite: LOCAL_DEV_HOST,
        onProxyReq,
        onProxyRes,
      },
]