◆

Webpack devServer 代理 Cookie 域名重写

2023-12-19 09:480

在开发环境中如果接口返回的 cookie 是指定的域名,如 test.com,但是我们本地开发需要使用这个 cookie 信息, 这时候我们需要把接口返回的 cookie 设置为本地开发环境的 localhost,配置如下

先说清楚为什么会出现这个问题。本地起的服务跑在 localhost:8080,接口通过 devServer 代理转发到 https://test.com。后端登录成功后下发 Set-Cookie,里面带着 Domain=.test.com 这个属性。浏览器收到这个响应头时会做一次校验:当前页面的域名是 localhost,和 cookie 声明的 .test.com 不匹配,属于「跨域设置 cookie」,直接静默丢弃。

丢弃是静默的,这是最坑的地方。Network 面板里能看到响应头明明带着 Set-Cookie,但 Application → Cookies 里空空如也,后续请求也不带 cookie,接口全返回 401。很容易误判成后端没下发 cookie。

解决思路就是在代理这一层把 Domain 改掉。响应从目标服务器回来、还没交给浏览器之前,把 Domain=.test.com 改写成 Domain=localhost,浏览器一看域名对得上就会正常存下来。

{
  devServer: {
    proxy: {
      '/api': {
          target: 'https://test.com',
          onProxyRes: (proxyRes, _req, _res) => {
            const cookies = proxyRes.headers['set-cookie'];
            if (cookies) {
              const cookieRegex = /domain=\.?test.com/i; // 返回的cookie中提取domain
              const newCookie = cookies.map(cookie => {
              if (cookieRegex.test(cookie)) {
                return cookie.replace(cookieRegex, 'domain=localhost');
              }
              return cookie;
            });

            delete proxyRes.headers['set-cookie'];
            proxyRes.headers['set-cookie'] = newCookie;
          }
        },
      },
    }
  }
}

onProxyRes 是 http-proxy-middleware 提供的钩子,在代理拿到目标服务器响应、转发给浏览器之前触发,这个时机正好用来改响应头。proxyRes.headers['set-cookie'] 是个数组(一次响应可能下发多个 cookie),所以要 map 逐条处理。

正则里的 \.? 是为了同时兼容 domain=test.com 和 domain=.test.com 两种写法,带点的表示包含子域。/i 标志是因为 cookie 属性名大小写不敏感,后端可能写成 Domain 也可能写成 domain。

更省事的写法

其实 http-proxy-middleware 内置了 cookieDomainRewrite 选项,不用自己写钩子:

proxy: {
  '/api': {
    target: 'https://test.com',
    changeOrigin: true,
    cookieDomainRewrite: 'localhost',
  },
}

传字符串就是把所有 cookie 的 domain 统一改成这个值,传 ''(空字符串)则是直接删掉 domain 属性——删掉之后浏览器会默认用当前请求的域名,效果一样且更干净。要精细控制的话可以传对象做映射:{ '.test.com': 'localhost', '*': '' }。

配套还有个 cookiePathRewrite,处理 cookie 的 Path 属性和本地路径不一致的情况。

还可能卡住的两个属性

domain 改对了 cookie 还是存不下来,一般是这两个属性在拦:

Secure 属性要求 cookie 只能通过 HTTPS 传输。本地是 http://localhost 的话,带 Secure 的 cookie 会被拒。不过 Chrome 对 localhost 做了特例放宽,实测多数情况下能存;如果不行,就在 onProxyRes 里把 ; Secure 一并去掉,或者给 devServer 开 https: true。

SameSite=None 必须和 Secure 一起用,否则浏览器直接丢弃。后端为了支持跨站场景常配成 SameSite=None; Secure,本地走 http 时这组合就废了。改写时把 SameSite=None 换成 SameSite=Lax 并去掉 Secure 能绕过去。

这些改写只应该出现在开发环境的配置里。生产环境的 cookie 属性由后端按真实域名下发,前端不该也没机会插手。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEWebpack devServer 代理 Cookie 域名重写加载中