You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨Docker容器请求时Fetch未携带JSESSIONID Cookie问题解决

问题描述

我有一个运行在独立Docker容器中的Spring Boot后端,使用JSESSIONID Cookie进行身份认证,该Cookie可在浏览器中查看。但运行在不同端口的Svelte.js前端容器通过fetch()请求后端时,似乎并未携带浏览器中的Cookie,尽管已设置{credentials:'include'}。

我的Svelte服务端代码(运行在Docker容器而非浏览器中):

/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
    const response = await fetch(`${getBaseUrl()}/api/draftgroups`,{credentials:'include',method:'get'});

    let txt = await response.text()
    if(response.headers.get("content-type") != "application/json") {
    // 判断用户是否登录:未登录会跳转至Google OAuth登录,返回HTML响应
       console.log("NOT SIGNED IN")
        throw redirect('302',`http://localhost:6868/login`)
    }
    if (!response.ok) {
        throw new Error(`Error! status: ${response.status}`);
    }

    let responseJSON = await response.json()

    return responseJSON
}

逻辑说明:通过检查响应内容类型判断用户是否登录,未登录时会跳转至Google OAuth登录并返回HTML响应。预期仅首次访问时触发该逻辑,但后续每次访问都会返回登录跳转的HTML,说明请求未携带已认证的Cookie。

疑问:这是否与两个Docker容器属于不同Cookie源有关?该如何修复?

我的docker-compose.yml配置:

# Based off of https://github.com/bezkoder/docker-compose-nodejs-mongodb/tree/master/bezkoder-app
# https://www.bezkoder.com/docker-compose-nodejs-mongodb/

version: "3.8"
services:
  mongodb:
    image: mongo:5.0.2
    restart: unless-stopped
    ports:
      - 27017:27017
    volumes:
      - db:/data/db

  spring-boot:
    image: bracket_backend
    build:
      context: ./backend
      dockerfile: Dev.Dockerfile
    depends_on:
      - mongodb
    ports:
      - 6868:8080
    stdin_open: true
    tty: true
    volumes: 
      - ./backend/src:/app/src
      
  frontend-svelte:
    image: bracket_frontend
    build:
      context: ./frontend-svelte
      dockerfile: Dev.Dockerfile
    ports:
      - 1234:8080
    stdin_open: true
    tty: true
    volumes:
      - ./frontend-svelte/src:/app/src
    depends_on:
      - spring-boot


volumes:
  db:
问题原因与修复方案

核心原因

问题完全由跨源Cookie规则和请求执行位置导致,两个关键点:

  1. PageServerLoad是在前端Docker容器内执行的,不是用户浏览器。此时的fetch请求是从前端容器发起的,根本访问不到用户浏览器中存储的JSESSIONID Cookie——浏览器Cookie只属于用户本地环境,和前端容器无关。
  2. 即使是浏览器端的fetch,当前localhost:1234(前端)和localhost:6868(后端)属于不同端口,属于跨源请求,Cookie跨源传输需要满足严格的规则。

分步修复

1. 将请求移至浏览器端执行

把数据获取逻辑从服务端的PageServerLoad转移到浏览器端(比如用onMount钩子),这样fetch是在用户浏览器中发起的,才能读取到浏览器存储的JSESSIONID Cookie。

示例代码(浏览器端):

<script>
  import { onMount } from 'svelte';
  import { getBaseUrl } from './utils';

  let draftGroups = [];

  onMount(async () => {
    try {
      const response = await fetch(`${getBaseUrl()}/api/draftgroups`, {
        credentials: 'include',
        method: 'GET'
      });

      if (response.headers.get('content-type') !== 'application/json') {
        window.location.href = 'http://localhost:6868/login';
        return;
      }

      if (!response.ok) {
        throw new Error(`请求失败:${response.status}`);
      }

      draftGroups = await response.json();
    } catch (err) {
      console.error(err);
    }
  });
</script>

<!-- 页面渲染内容 -->
{#if draftGroups.length}
  {#each draftGroups as group}
    <div>{group.name}</div>
  {/each}
{:else}
  <p>加载中...</p>
{/if}

2. 配置Spring Boot后端的CORS支持

在Spring Boot中配置CORS,允许前端源的请求携带Cookie:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.List;

@Configuration
public class CorsConfig {
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        // 允许前端的源(开发环境为localhost:1234)
        config.setAllowedOrigins(List.of("http://localhost:1234"));
        // 允许的请求方法
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        // 允许的请求头
        config.setAllowedHeaders(List.of("*"));
        // 允许携带凭证(Cookie)
        config.setAllowCredentials(true);
        // 暴露响应头中的Cookie相关信息,让浏览器可以读取
        config.setExposedHeaders(List.of("Set-Cookie"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对所有路径应用CORS配置
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

3. 优化JSESSIONID Cookie的设置(推荐)

在Spring Boot的配置文件application.properties中,调整Cookie的属性,确保跨源场景下能正常传递:

# 设置Cookie的SameSite属性,开发环境用Lax即可
server.servlet.session.cookie.same-site=Lax
# 指定Cookie的作用域为localhost(本地开发)
server.servlet.session.cookie.domain=localhost
# 确保Cookie在HTTPS环境下也能传递(生产环境启用)
# server.servlet.session.cookie.secure=true

4. (可选)用反向代理统一域名

如果不想处理跨域问题,可以新增一个Nginx服务作为反向代理,将前端和后端映射到同一个域名的不同路径下,比如:

  • http://localhost/api/ 转发至Spring Boot后端(http://spring-boot:8080/)
  • http://localhost/ 转发至Svelte前端(http://frontend-svelte:8080/)

这样前端和后端同域,Cookie可以自动携带,无需处理跨源问题。对应的docker-compose.yml新增Nginx服务:

nginx:
  image: nginx:alpine
  ports:
    - 80:80
  volumes:
    - ./nginx.conf:/etc/nginx/conf.d/default.conf
  depends_on:
    - spring-boot
    - frontend-svelte

nginx.conf配置示例:

server {
    listen 80;
    server_name localhost;

    # 后端API转发
    location /api/ {
        proxy_pass http://spring-boot:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # 前端页面转发
    location / {
        proxy_pass http://frontend-svelte:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

内容的提问来源于stack exchange,提问作者Ethan Michel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:09:59