Cookie 操作

Cookie 是 Web 开发中常用的状态保持机制,Gin 提供了便捷的 Cookie 操作方法。

设置 Cookie

使用 SetCookie 方法:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    
    r.GET("/set-cookie", func(c *gin.Context) {
        c.SetCookie(
            "username",
            "张三",
            3600,
            "/",
            "localhost",
            false,
            true,
        )
        
        c.String(http.StatusOK, "Cookie 已设置")
    })
    
    r.Run(":8080")
}

SetCookie 参数说明:

  1. name - Cookie 名称
  2. value - Cookie 值
  3. maxAge - 过期时间(秒),-1 表示删除,0 表示会话 Cookie
  4. path - 有效路径
  5. domain - 域名
  6. secure - 是否只在 HTTPS 下传输
  7. httpOnly - 是否禁止 JavaScript 访问

获取 Cookie

r.GET("/get-cookie", func(c *gin.Context) {
    username, err := c.Cookie("username")
    if err != nil {
        c.String(http.StatusOK, "没有找到 Cookie")
        return
    }
    
    c.String(http.StatusOK, "用户名: %s", username)
})

获取所有 Cookie

r.GET("/all-cookies", func(c *gin.Context) {
    cookies := c.Request.Cookies()
    
    result := make(map[string]string)
    for _, cookie := range cookies {
        result[cookie.Name] = cookie.Value
    }
    
    c.JSON(http.StatusOK, result)
})

删除 Cookie

r.GET("/delete-cookie", func(c *gin.Context) {
    c.SetCookie(
        "username",
        "",
        -1,
        "/",
        "",
        false,
        true,
    )
    
    c.String(http.StatusOK, "Cookie 已删除")
})

Cookie 认证示例

func AuthCookie() gin.HandlerFunc {
    return func(c *gin.Context) {
        token, err := c.Cookie("auth_token")
        if err != nil || token == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "请先登录",
            })
            return
        }
        
        user, err := validateToken(token)
        if err != nil {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "登录已过期",
            })
            return
        }
        
        c.Set("currentUser", user)
        c.Next()
    }
}

r.POST("/login", func(c *gin.Context) {
    var login struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }
    
    if err := c.ShouldBindJSON(&login); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    if login.Username != "admin" || login.Password != "123456" {
        c.JSON(401, gin.H{"error": "用户名或密码错误"})
        return
    }
    
    token := generateToken(login.Username)
    
    c.SetCookie(
        "auth_token",
        token,
        3600*24*7,
        "/",
        "",
        false,
        true,
    )
    
    c.JSON(200, gin.H{"message": "登录成功"})
})

r.GET("/profile", AuthCookie(), func(c *gin.Context) {
    user, _ := c.Get("currentUser")
    c.JSON(200, user)
})

r.POST("/logout", func(c *gin.Context) {
    c.SetCookie("auth_token", "", -1, "/", "", false, true)
    c.JSON(200, gin.H{"message": "已退出登录"})
})

安全的 Cookie 设置

生产环境的安全配置:

func setSecureCookie(c *gin.Context, name, value string, maxAge int) {
    c.SetCookie(
        name,
        value,
        maxAge,
        "/",
        "",
        true,
        true,
    )
}

r.GET("/secure", func(c *gin.Context) {
    setSecureCookie(c, "session", "abc123", 3600)
    c.String(200, "安全 Cookie 已设置")
})

SameSite 属性

Go 1.11+ 支持 SameSite 属性:

r.GET("/samesite", func(c *gin.Context) {
    http.SetCookie(c.Writer, &http.Cookie{
        Name:     "session",
        Value:    "abc123",
        Path:     "/",
        MaxAge:   3600,
        HttpOnly: true,
        Secure:   true,
        SameSite: http.SameSiteStrictMode,
    })
    
    c.String(200, "SameSite Cookie 已设置")
})

SameSite 值:

  • http.SameSiteDefaultMode - 默认值,等同于 Lax
  • http.SameSiteLaxMode - 允许顶级导航携带 Cookie
  • http.SameSiteStrictMode - 完全禁止跨站发送
  • http.SameSiteNoneMode - 允许跨站发送(需要 Secure)

Cookie 编码

Cookie 值需要编码处理特殊字符:

import "net/url"

r.GET("/encode-cookie", func(c *gin.Context) {
    value := url.QueryEscape("张三&李四")
    c.SetCookie("names", value, 3600, "/", "", false, true)
    c.String(200, "编码 Cookie 已设置")
})

r.GET("/decode-cookie", func(c *gin.Context) {
    encoded, err := c.Cookie("names")
    if err != nil {
        c.String(200, "没有 Cookie")
        return
    }
    
    decoded, _ := url.QueryUnescape(encoded)
    c.String(200, "解码后: %s", decoded)
})

小结

Gin 的 Cookie 操作简单直观,SetCookieCookie 方法足够日常使用。生产环境记得设置 HttpOnlySecure 属性,有条件的话也设置 SameSite,提高安全性。