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