登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go http.Client 重定向时 CookieJar 如何判断目标域

来源:17golang原创

时间:2026-09-12 11:36:43 464浏览 收藏

Go 的 http.Client 遇到 301、302、303、307 或 308 时,是否把会话 Cookie 带到新地址,不是简单地把上一跳的请求头原样复制过去。只要配置了非空的 CookieJar,客户端会在每个重定向目标上重新咨询 Jar;Jar 再根据目标主机、Cookie 的 Domain、Path、Secure 和有效期决定返回哪些 Cookie。

要点速览
  • CookieJar 判断的是“当前要发往哪里”,不是最初请求从哪里来。
  • 没有 Domain 属性的 Cookie 是 host-only,只回到设置它的主机;Domain=example.com 才可能覆盖其子域。
  • CheckRedirect 负责是否继续跟随,不能代替 CookieJar 的域名匹配;排查时要同时看两层。

先把重定向和 Cookie 选择拆成两层

http.Client 先收到上一跳的响应,再准备一个即将发往新 URL 的请求。如果设置了 CheckRedirect,它会在发送下一跳之前被调用,参数里的 req 就是即将发送的请求,via 保存此前的请求链。回调返回错误会停止跟随,返回 http.ErrUseLastResponse 则保留最近一次响应。

是否继续是一层,Cookie 是否适合这个新 URL 是另一层。Client.Jar 非空时,Jar 会为每个出站请求提供 Cookie,也会接收每个响应的 Set-Cookie。因此从 login.example.com 跳到 app.example.com 时,真正参与选择的是后者这个目标 URL。

Go http.Client 重定向中 CookieJar.Cookies 根据目标 URL、Domain、Path 和 Secure 生成 Cookie 头的静态关系图
图1:CookieJar 以每次重定向的目标 URL 为输入,同时考虑 Domain、Path 和 Secure,得到当前请求可携带的 Cookie。

用最小探针看懂 host-only 与 Domain

先不要在业务服务里猜。下面的探针只使用标准库,把两种 Cookie 放进 Jar,再逐个查询不同目标。Cookie 没写 Domain 时,SetCookies 会把它作为 host-only Cookie 保存。

package main

import (
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
)

func main() {
	// 使用标准 CookieJar,观察不同目标 URL 得到的 Cookie 集合。
	jar, err := cookiejar.New(nil)
	if err != nil {
		panic(err)
	}

	origin, _ := url.Parse("https://login.example.com/start")
	jar.SetCookies(origin, []*http.Cookie{
		// 没有 Domain,只能回到 login.example.com。
		{Name: "host_sid", Value: "host-value", Path: "/"},
		// 父域 Cookie 可以匹配 example.com 的子域。
		{Name: "domain_sid", Value: "domain-value", Domain: "example.com", Path: "/"},
	})

	for _, raw := range []string{
		"https://login.example.com/callback",
		"https://app.example.com/callback",
		"https://api.other.com/callback",
	} {
		target, _ := url.Parse(raw)
		// Cookies 的结果就是该目标请求适合携带的 Cookie。
		fmt.Printf("%s -> %v\\n", target.Host, jar.Cookies(target))
	}
}

预期的判断是:回到 login.example.com 时两枚 Cookie 都可能返回;去 app.example.com 时只有带 Domain=example.com 的那枚匹配;api.other.com 与两者都无域名关系。这里的“可能”还要留给 Path、Secure、过期时间和公共后缀策略继续筛选。

目标域匹配还要过 Path 与 Secure 两道检查

cookiejar 的发送判断可以概括为 domainMatch && pathMatch && secureMatch。域名相等时可以匹配;如果 Cookie 不是 host-only,目标主机还必须以 .Domain 结尾。它不会把 example.com.evil.test 当成 example.com 的子域。

条件判断重点常见现象
Domain未设置就是 host-only;设置父域才覆盖子域跨子域跳转后会话消失
Path请求路径必须落在 Cookie 的路径范围内域名正确但某个回调路径没有 Cookie
SecureSecure Cookie 需要 HTTPS;localhost 有特殊处理开发环境 HTTP 请求看不到 Cookie
Jar非空 Jar 每跳重新选;nil Jar 不管理 Cookie手写 Cookie 头与 Jar 结果不一致

还要注意重定向的敏感头策略:Go 不会把 CookieAuthorization 等敏感头无条件发给不可信目标。非空 Jar 下,客户端会去掉可能已经过时的手写 Cookie,再让 Jar 按新 URL 插入匹配项。若 Jar 为 nil,初始请求显式设置的 Cookie 会按客户端规则保持原样,这也是手工测试和生产代码表现不同的常见原因。

Go CookieJar 中 login.example.com、example.com、app.example.com 与 api.other.com 的 host-only 和 Domain Cookie 边界关系图
图2:host-only Cookie 只绑定设置它的主机;带 Domain=example.com 的 Cookie 才可能覆盖 example.com 的子域,api.other.com 在边界之外。

用 CheckRedirect 定位 Cookie 消失在哪一跳

当服务端声称“已经登录”,但最终页面又变成未登录,先把每一跳的目标和请求头记录下来。不要在日志中打印真实会话值,下面只输出 Cookie 名称,避免把凭据写进日志。

func newClient(jar http.CookieJar) *http.Client {
	client := &http.Client{
	Jar: jar,
	CheckRedirect: func(req *http.Request, via []*http.Request) error {
		// 只记录目标主机和 Cookie 名称,不泄露 Cookie 值。
		fmt.Printf("redirect #%d target=%s cookie=%s\n",
			len(via)+1, req.URL.Host, req.Header.Get("Cookie"))
		// nil 表示允许 Client 继续处理这一跳。
		return nil
	},
}
	return client
}

func fetch(client *http.Client) error {
	// 使用 client.Get 发起请求;响应 Body 仍需由调用方关闭。
	resp, err := client.Get("https://login.example.com/start")
	if err != nil {
		// CheckRedirect 错误和网络错误都从这里返回。
		return err
	}
	defer resp.Body.Close()
	return nil
}

如果 req.URL.Host 已经变成目标域,但 Cookie 为空,优先检查 Jar 的 Domain、Path、Secure 和过期状态;如果请求根本没有进入下一跳,则看 CheckRedirect 是否返回了错误。两类问题不要混在一起改。

生产代码的验收清单

认证跳转涉及多个子域时,先明确会话 Cookie 应该绑定单一主机还是父域,再让服务端发出对应的 Set-Cookie。客户端不要为了“让它工作”在每次重定向前复制完整 Cookie 头,也不要在 CheckRedirect 里绕开目标域检查。

  • 确认起始响应和每一跳的 URL、协议、Host 与 Path。
  • 确认 Cookie 是否写了 Domain,是否意外设置成过窄的 Path。
  • 确认 Secure Cookie 没有被 HTTP 开发回调误判为丢失。
  • 确认 Client 复用同一个 Jar,并且自定义 Jar 满足并发安全要求。

常见问题

CookieJar 会按照初始 URL 判断 Cookie 吗?

不会。跟随重定向时,Jar 针对每个即将发送的目标 URL 返回 Cookie;初始 URL 只影响它之前收到和保存的 Cookie。

跳到同一父域的子域,为什么 host-only Cookie 仍然没有?

因为 host-only Cookie 只属于设置它的主机。即使 app.example.comlogin.example.com 共享父域,也不会自动共享未设置 Domain 的 Cookie。

CheckRedirect 里手动加 Cookie 能解决问题吗?

不建议。它容易把会话发送到错误域,且会和非空 Jar 的自动选择产生冲突。应修正服务端 Domain、Path 或协议配置,再用回调只做目标和 Cookie 名称级别的诊断。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>