解决本地前端向远程服务端发送跨域请求时,cookie无法携带问题

环境

  • 前端:vue3 + vite
  • 后端:SpringBoot 3.3.4

场景描述:在本地启动前端项目时,调用远程服务端接口登录成功后,发现session的登录态丢失

问题分析:前端[http://localhost:5173/]调用后端接口[http://154.8.193.216:7777/api],属于跨域请求,因此我在服务端加上了CORS跨域配置,其中设置了 config.setAllowCredentials(true),表示允许前端在发送跨域请求时携带凭证(cookie), 并且前端Axios配置中加上了withCredentials: true,请求时会带上cookie凭证,但实际上在浏览器Network面板上发现当login登录接口调用成功后,紧接着调用的queryAll接口的请求头中没有cookie这一项,说明了发送请求时浏览器并没有带上cookie,并且在响应头中的Set-Cookie这一项出现了警告

Set-Cookie————JSESSIONID=C57AFC4D279D8FFF8904348A2B214112; Max-Age=2592000; Expires=Tue, 19 Aug 2025 14:24:02 GMT; Path=/api; HttpOnly

此Set-Cookie标头未指定”SameSite”属性,它默认为”SameSite=Lax,”并被阻止,因为它来自跨站点响应,而不是对顶级导航的响应。必须为此Set-Cookie设置“SameSite=None”才能实现跨站点使用。

这个警告是说:浏览器在处理跨站点的 AJAX/Fetch 响应时,如果它看到 Set‑Cookie 里没有 SameSite=None,就会把这条 Cookie 当成 SameSite=Lax,直接拒绝存储或发送给跨站请求。

SameSite(Cookie属性)

  • 作用域:它是一个 Cookie 的属性,用来告诉浏览器「在什么场景下,可以把这个 Cookie 附带到请求里发送」

  • 取值:

    • Strict:跨站点(cross‐site)的任何请求都不发送,只有顶级导航才会。

    • Lax (默认):允许顶级导航的 GET 请求带 Cookie,但 AJAX/fetch/POST 等子资源请求不带。

    • None:放行所有请求(包括跨站点的 AJAX),但必须配合 Secure(HTTPS)才能生效。

从上面我们可以看到当前响应头中的Set-Cookie里没有SameSite属性,因此SameSite取默认值Lax,而Lax属性值表示在跨域请求下,无法携带cookie, 只有SameSite取值为None时,才能跨站点发送请求, 因此我首先想到的解决方案是在服务端中的cookie配置中加上same-site:none,这样响应头中的Set-Cookie就会被写入SameSite=None这一项

session:
  cookie:
    max-age: 2592000
    same-site: none

重新部署测试后,发现响应头中确实加上了SameSite这一项,不过queryAll接口的请求头中仍然没有携带cookie,问题还是未能得到解决

Set-Cookie————JSESSIONID=D1C5452C6A1F9E43453ECF32F13C6E23; Max-Age=2592000; Expires=Tue, 19 Aug 2025 15:17:32 GMT; Path=/api; HttpOnly; SameSite=None

但是出现了新的警告:尝试通过Set-Cookie标头设置Cookie时被阻止,因为它具有”SameSite=None”属性,但没有使用"SameSite=None"所必需的"Secure"属性。

这个警告是浏览器在执行 Set‑Cookie 时的一道硬性校验:只要你给 Cookie 标记了 SameSite=None,就必须同时加上 Secure,否则浏览器会一概拒绝。

因此我在服务端的配置文件中又加上一项secure:true

session:
  cookie:
    max-age: 2592000
    same-site: none
    secure: true

重新部署测试后,响应头中确实加上了Secure,不过queryAll接口的请求头中仍然没有携带cookie,问题还是未能得到解决

Set-Cookie————JSESSIONID=A444FB229F2DD86BB7C6B8B8589E72CF; Max-Age=2592000; Expires=Tue, 19 Aug 2025 15:35:32 GMT; Path=/api; Secure; HttpOnly; SameSite=None

但又出现了新的警告:尝试通过Set-Cookie标头设置Cookie时被阻止,因为它具有”Secure”属性,但未通过安全连接发送。

这个警告说的是浏览器只允许在“安全上下文”(HTTPS 或者 localhost/127.0.0.1)下设置带 Secure 属性的 Cookie,任何通过 HTTP(非加密连接)下发的带 Secure 的 Set‑Cookie 都会被直接丢弃

我的后端接口地址使用的是http(非https),所以通过在后端配置ssl证书来启用https,可以解决请求无法携带cookie的问题(由于条件有限,这里不做演示)

通过以上实验,我们可以看出如果要在跨域请求中携带cookie,响应头中的Set-Cookie必须包含SameSite=None,而添加了SameSite=None就需要配合Secure,使用了Secure又必须保证请求是Https

本地前端调用远程服务端接口,需要后端配置ssl证书来启用Https,这种方法太麻烦了,有没有其他解决方案呢?

在这之前,首先我们需要知道一个非常重要的概念,就是浏览器对“同一站点”(same‐site)和“跨站点”(cross‐site)的请求会有两套不同的 Cookie 发送规则

same-site请求(注册域名相同,不论端口)—>默认就能带 Cookie
cross-site请求(注册域名不同)->会被浏览器默认的SameSite=Lax拦截,必须SameSite=None; Secure + HTTPS + 前端withCredentials: true + 后端CORSallowCredentials(true)才能携带cookie

我这里属于cross-site(跨站点)请求,因此上述采用的是跨站点请求的cookie携带规则,这也就是为什么本地前端调用本地后端,远程前端调用远程后端(同一主机)可以携带cookie的原因,因为它们都属于same-site(同站点)请求,因此下面的第二种解决方案我们通过“同源代理”来发送same-site(同站点)请求

同源代理”就是利用本地开发服务器做一个反向代理,把发到[http://localhost:5173/api/...]的请求转发到真正的后端[http://154.8.193.216:7777/api...],这样就能保证请求的主机名始终是localhost,浏览器看见的URL都是http://localhost:5173,就认为它和页面同源,不触发跨域检查,具体代码实现如下

// myAxios.ts
const myAxios = axios.create({
    baseURL: '/api',
    withCredentials:true,
});
// vite.config.ts
server: {
  proxy: {
    // 将 /api 前缀的请求代理到后端,并映射到后端的 /api 路径
    '/api': {
      target: 'http://154.8.193.216:7777/api',
      changeOrigin: true,
      // 去掉本地请求的 /api 前缀后再发送给后端
      rewrite: (path) => path.replace(/^\/api/, '')
    },
  },
}

此时在浏览器中可以看到当前请求的请求头中带上了cookie,问题得到解决

除了上面两种方案,还有一种解决方案是更换火狐浏览器,和Edge, Chrome浏览器不同,火狐浏览器Cookie不会受到SameSite=Lax的限制,可以在发送跨站点请求时正常携带cookie

或者不使用Cookie-Session的登录模式,改用Token + Authorization,登录成功后后端返回 JWT(写在响应体里),前端存到 localStorage 或内存, 后续接口通过 Authorization: Bearer 头传递,这样可以完全绕开Cookie的跨域限制

总结:本地前端向远程服务端发送请求时,要想携带cookie,可采用一下四种方案:

1. 给后端开HTTPS

2. 本地开发用“同源代理”

3. 使用火狐浏览器

4. 改用Token + Authorization

posted @ 2025-07-21 14:01  chenxinzhi  阅读(39)  评论(0)    收藏  举报