Cookie 与会话基础:SetCookie/GetCookie

本章解决什么问题:HTTP 无状态,但“记住登录用户、记住偏好”需要状态。Cookie 是最小粒度的状态载体:服务器下发、浏览器回传。本章讲读写 Cookie 的 API、属性语义,以及“签名 cookie”的思路。

写入与读取

c.SetCookie 的参数依次是名称、值、有效期(秒)、路径、域名、secure、httpOnly;取用时 c.Cookie(name) 返回 (值, error),不存在时 err 非空,也可用 c.GetCookie(name) 拿布尔值判断是否存在:

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	r.GET("/set", func(c *gin.Context) {
		c.SetSameSite(http.SameSiteLaxMode) // 先定 SameSite,再写 Cookie
		c.SetCookie("user", "alice", 3600, "/", "", false, true)
		c.String(200, "cookie set")
	})
	r.GET("/get", func(c *gin.Context) {
		u, err := c.Cookie("user")
		if err != nil {
			c.String(http.StatusNotFound, "no cookie")
			return
		}
		c.String(200, "hello "+u)
	})
	r.Run(":8080")
}

清掉 Cookie 的一种方式是重写同名 Cookie 并把有效期设为负数(如 -1),浏览器收到后即删除。

属性语义

  • maxAge:有效秒数;设为负数表示会话级,关浏览器即失效;
  • path/domain:Cookie 生效的路径与域名范围,跨子域共享需要正确设置 domain;
  • secure:只允许 HTTPS 传输;
  • httpOnly:禁止 JavaScript 读取,降低 XSS 窃取风险,能开则开;
  • SameSite:限制跨站携带,Lax 是兼顾兼容的常用档位。

其中 secure 与 httpOnly 对会话类 Cookie 几乎应当恒开。

会话与签名 Cookie 的思路

Cookie 本体放在客户端,能被用户看到甚至篡改,因此不能直接放敏感数据。常见进阶做法:

  • 会话式:Cookie 只存一个随机 session id,真正的数据放服务端(内存、Redis 或数据库),这就是 session 机制;gin-contrib/sessions 提供现成方案;
  • 签名式:让 Cookie 内容“可读但不可改”——用密钥对内容做 HMAC,把签名附在值后面,读取时重算比对:
func sign(raw string) string {
	mac := hmac.New(sha256.New, secret) // secret 为服务端密钥 []byte
	_, _ = mac.Write([]byte(raw))
	return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}

写入时存 raw + "." + sign(raw),读取时验签,验不过就丢弃。注意签名只保证完整性,不加密内容;要防窥探得再加加密层。cookie 有 4KB 左右的容量限制,别放太多东西。

关键点

  • 写 Cookie 用 SetCookie 全家桶参数,读用 c.Cookie/c.GetCookie,清空用负 maxAge。
  • httpOnly、secure、SameSite 是会话 Cookie 的三个必选项。
  • 客户端内容不可信:签名保完整、加密保机密、敏感数据请走服务端 session。

小结

Cookie 是“服务器写、浏览器存、随请求回传”的状态载体,属性与安全边界要清楚。下一章把响应与错误做成统一外壳,收拢接口风格。

笔记加载中…