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

登录成功后如何在Golang中提取响应Cookie值?代码问题排查

问题修复方案

核心问题分析

代码出现后续请求重定向回登录页的原因主要有两点:

  • 登录请求使用不完整URL,导致Cookie被存储在错误域名下,后续请求目标域名时无法携带有效会话Cookie
  • 自定义Cookie Jar实现存在缺陷,未正确处理同名Cookie的更新与路径匹配逻辑

具体修复步骤

  1. 修正登录请求的完整URL
    原代码中client.PostForm("domain.com", ...)使用了不完整的URL,会导致请求发送到http://domain.com而非目标HTTPS地址,Cookie被绑定到错误域名。需替换为完整的登录接口地址(可通过浏览器开发者工具查看登录表单的action属性确认):

    resp, err = client.PostForm("https://developer.domain.com.br/pt-BR/sign-in", url.Values{
        "UserLogin[email]":    {"xxxx"},
        "UserLogin[password]": {"yyyyy"},
        "csrf_token":          {csrfToken},
    })
    
  2. 修复自定义Cookie Jar的实现
    原Jar的SetCookies方法仅追加Cookie,未处理同名Cookie的更新;Cookies方法未过滤路径不匹配的Cookie。替换为严谨的实现:

    type Jar struct {
        sync.Mutex
        cookies map[string][]*http.Cookie
    }
    
    func NewJar() *Jar {
        return &Jar{
            cookies: make(map[string][]*http.Cookie),
        }
    }
    
    func (jar *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) {
        jar.Lock()
        defer jar.Unlock()
    
        host := u.Host
        existingCookies := jar.cookies[host]
        updated := make([]*http.Cookie, 0)
    
        // 遍历新Cookie,替换同名同路径的旧Cookie
        for _, newCookie := range cookies {
            found := false
            for _, existing := range existingCookies {
                if existing.Name == newCookie.Name && existing.Path == newCookie.Path {
                    updated = append(updated, newCookie)
                    found = true
                    break
                }
            }
            if !found {
                updated = append(updated, newCookie)
            }
        }
        jar.cookies[host] = updated
    }
    
    func (jar *Jar) Cookies(u *url.URL) []*http.Cookie {
        jar.Lock()
        defer jar.Unlock()
    
        host := u.Host
        cookies := jar.cookies[host]
        valid := make([]*http.Cookie, 0)
        // 过滤出路径匹配的Cookie
        for _, c := range cookies {
            if strings.HasPrefix(u.Path, c.Path) {
                valid = append(valid, c)
            }
        }
        return valid
    }
    
  3. 完善资源释放与错误处理
    每个HTTP响应的Body必须关闭以避免资源泄漏;同时不能忽略请求创建的错误:

    // 获取CSRF Token的请求示例
    req, err := http.NewRequest("GET", "https://developer.domain.com.br/pt-BR/sign-in", nil)
    if err != nil {
        log.Fatal(err)
    }
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close() // 必须关闭响应Body
    

完整修复后的代码

package main

import (
    "fmt"
    "github.com/PuerkitoBio/goquery"
    "io"
    "log"
    "net/http"
    "net/url"
    "strings"
    "sync"
)

type Jar struct {
    sync.Mutex
    cookies map[string][]*http.Cookie
}

func NewJar() *Jar {
    return &Jar{
        cookies: make(map[string][]*http.Cookie),
    }
}

func (jar *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) {
    jar.Lock()
    defer jar.Unlock()

    host := u.Host
    existingCookies := jar.cookies[host]
    updated := make([]*http.Cookie, 0)

    for _, newCookie := range cookies {
        found := false
        for _, existing := range existingCookies {
            if existing.Name == newCookie.Name && existing.Path == newCookie.Path {
                updated = append(updated, newCookie)
                found = true
                break
            }
        }
        if !found {
            updated = append(updated, newCookie)
        }
    }
    jar.cookies[host] = updated
}

func (jar *Jar) Cookies(u *url.URL) []*http.Cookie {
    jar.Lock()
    defer jar.Unlock()

    host := u.Host
    cookies := jar.cookies[host]
    valid := make([]*http.Cookie, 0)
    for _, c := range cookies {
        if strings.HasPrefix(u.Path, c.Path) {
            valid = append(valid, c)
        }
    }
    return valid
}

func NewJarClient() *http.Client {
    return &http.Client{
        Jar: NewJar(),
    }
}

func fetch(w http.ResponseWriter, r *http.Request) {
    client := NewJarClient()

    // 获取CSRF Token
    req, err := http.NewRequest("GET", "https://developer.domain.com.br/pt-BR/sign-in", nil)
    if err != nil {
        log.Fatal(err)
    }
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    doc, err := goquery.NewDocumentFromResponse(resp)
    if err != nil {
        log.Fatal(err)
    }

    csrfToken := ""
    if val, ok := doc.Find(`head meta[name="csrf-token-value"]`).Attr("content"); ok {
        csrfToken = val
    }

    // 发送登录请求(修正完整URL)
    resp, err = client.PostForm("https://developer.domain.com.br/pt-BR/sign-in", url.Values{
        "UserLogin[email]":    {"xxxx"},
        "UserLogin[password]": {"yyyyy"},
        "csrf_token":          {csrfToken},
    })
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    cookies := resp.Cookies()
    fmt.Println("获取到的Cookie:")
    for _, c := range cookies {
        fmt.Printf("名称: %s, 值: %s\n", c.Name, c.Value)
    }

    doc, err = goquery.NewDocumentFromResponse(resp)
    if err != nil {
        log.Fatal(err)
    }

    // 请求门户页面
    req, err = http.NewRequest("GET", "https://developer.domain.com.br/pt-BR/developer/profile", nil)
    if err != nil {
        log.Fatal(err)
    }
    resp, err = client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    fmt.Println("响应状态:", resp.Status)
    doc, err = goquery.NewDocumentFromResponse(resp)
    if err != nil {
        log.Fatal(err)
    }

    html, _ := doc.Html()
    io.WriteString(w, html)
}

func main() {
    http.HandleFunc("/", fetch)
    log.Println("服务启动在 127.0.0.1:49721")
    log.Fatal(http.ListenAndServe("127.0.0.1:49721", nil))
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:14:53