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

在SvelteKit中通过外部Golang API设置JWT Cookie失败的问题

问题描述

在SvelteKit应用中调用外部Golang API完成登录并设置JWT Cookie,代码执行无报错,但浏览器中始终看不到设置的Cookie。想确认是否无法通过外部API设置JWT Cookie?由于大部分业务逻辑都在该外部API中,希望通过它完成认证流程。

SvelteKit 前端代码

async function OnSubmit() {
    //test
    let res = await fetch('http://localhost:50000/login', {
        credentials: 'same-origin',
        method: 'POST',
        mode: 'cors',
        body: JSON.stringify({
            Username: 'raka',
            Password: 'password1'
        })
    });
    console.log('🚀 ~ file: index@blank.svelte ~ line 45 ~ OnSubmit ~ res', res);
    if (res.ok) {
        console.log('🚀 ~ file: index.svelte ~ line 49 ~ OnSubmit ~ value : ', 200);
    }
}

Golang 后端API代码

func main() {
    http.HandleFunc("/login", handler.Login)
    http.HandleFunc("/home", handler.Home)
    http.HandleFunc("/", handler.Home2)
    http.HandleFunc("/refresh", handler.Refresh)

    fmt.Println("listening in port 50000")
    log.Fatal(http.ListenAndServe(":50000", nil))
}

func Login(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
    fmt.Println("Login is hit ...")
    var credentials Credentials

    err := json.NewDecoder(r.Body).Decode(&credentials)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    expectedPassword, ok := Users[credentials.Username]
    if !ok || expectedPassword != credentials.Password {
        w.WriteHeader(http.StatusUnauthorized)
        return
    }

    expirationTime := time.Now().Add(time.Minute * 5)
    claims := &Claims{
        Username: credentials.Username,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenString, err := token.SignedString(jwtKey)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    http.SetCookie(w, &http.Cookie{
        Name:    "token",
        Value:   tokenString,
        Expires: expirationTime,
    })
}
问题原因及解决方案

不是不能通过外部API设置Cookie,而是你的跨域(CORS)配置和请求参数存在问题,导致浏览器拒绝存储Cookie:

1. CORS 配置错误

当Access-Control-Allow-Origin设为*时,浏览器会禁止携带credentials(包括Cookie),这是安全限制。必须将其改为前端的具体域名(比如SvelteKit默认的http://localhost:5173),同时添加Access-Control-Allow-Credentials: true头,允许跨域携带凭证。

2. Fetch 请求的 credentials 参数错误

当前设置的credentials: 'same-origin'只在同域请求时生效,跨域场景下需要改为credentials: 'include',这样浏览器才会在请求中携带Cookie,并且接收服务器返回的Cookie。

建议给Cookie添加HttpOnly(防止XSS攻击窃取Cookie)、SameSite(控制跨站Cookie发送规则,可选Lax/Strict,如果是HTTPS环境可以用None但必须配合Secure)属性,提升安全性同时避免浏览器拦截。


修改后的代码示例

调整后的Golang Login接口

func Login(w http.ResponseWriter, r *http.Request) {
    // 替换成你的SvelteKit前端实际地址
    w.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173")
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
    // 允许跨域携带凭证
    w.Header().Set("Access-Control-Allow-Credentials", "true")
    
    fmt.Println("Login is hit ...")
    var credentials Credentials

    err := json.NewDecoder(r.Body).Decode(&credentials)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    expectedPassword, ok := Users[credentials.Username]
    if !ok || expectedPassword != credentials.Password {
        w.WriteHeader(http.StatusUnauthorized)
        return
    }

    expirationTime := time.Now().Add(time.Minute * 5)
    claims := &Claims{
        Username: credentials.Username,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenString, err := token.SignedString(jwtKey)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    http.SetCookie(w, &http.Cookie{
        Name:     "token",
        Value:    tokenString,
        Expires:  expirationTime,
        HttpOnly: true, // 禁止JS读取,防范XSS攻击
        SameSite: http.SameSiteLaxMode, // 兼容大多数业务场景
        Path:     "/", // 让Cookie在所有路径下生效
        // 如果是HTTPS部署环境,添加以下配置
        // Secure: true,
    })
}

调整后的SvelteKit Fetch代码

async function OnSubmit() {
    let res = await fetch('http://localhost:50000/login', {
        credentials: 'include', // 跨域时允许携带并接收Cookie
        method: 'POST',
        mode: 'cors',
        headers: {
            'Content-Type': 'application/json', // 显式设置请求内容类型
        },
        body: JSON.stringify({
            Username: 'raka',
            Password: 'password1'
        })
    });
    console.log('🚀 ~ OnSubmit ~ res', res);
    if (res.ok) {
        console.log('登录成功');
    }
}

额外说明

  • 如果前端和后端部署在不同域名下,Access-Control-Allow-Origin必须设置为前端的实际域名,不能使用*
  • 如果使用SameSite: None,必须同时设置Secure: true,且网站必须采用HTTPS协议,否则浏览器会拒绝存储Cookie

内容的提问来源于stack exchange,提问作者sk shahriar ahmed raka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 08:15:44