Cookie

在 Gin 中,使用 Cookie 涉及到两个方法,Context.Cookie()Context.SetCookie()

func SetCookie(c *gin.Context) {
	// 设置 Cookie
	c.SetCookie("gin-cookie", "test", 3600, "/", "localhost", false, false)
	c.JSON(http.StatusOK, gin.H{
		"code": http.StatusOK,
		"data": value,
	})
}
func GetCookie(c *gin.Context) {
	// 获取 Cookie
	if value, err := c.Cookie(key); err != nil {
		c.Status(http.StatusBadRequest)
	} else {
		c.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"data": value,
		})
	}
}

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

	r.POST("/cookie", SetCookie)
	r.GET("/cookie", GetCookie)

	if err := r.Run(":8080"); err != nil {
		logrus.Error(err)
	}
}

其中:

  • Context.Cookie():获取指定名称的 Cookie。

  • Context.SetCookie():设置 Cookie。它的方法签名如下:

    func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
    	// ...
    }
    
    
    数解释如下:
    
    `name`:设置 Cookie 的名称。
    `value`:设置 Cookie 的值。
    `maxAge`:Cookie 的最大存活时间,单位为秒。如果为负数,则表示会话 Cookie(在浏览器关闭之后删除);如果为零,则表示立即删除Cookie。
    `path`:Cookie 的生效路径。如果为空字符串,则使用当前请求的 URI 路径作为默认值。如果是 `"/"`,那么所有路径都访问该 Cookie。
    `domain`:Cookie 的生效域。如果为空字符串,则不设置域名。
    `secure`:Cookie 是否仅用于 HTTPS 连接。如果为 `true`,则仅通过 HTTPS 连接发送 Cookie;否则,使用 HTTP 或 HTTPS 连接都可以发送 Cookie。
    `httpOnly`:Cookie 是否允许通过客户端程序访问。如果为 `true`,则无法通过客户端程序访问 Cookie;否则,可以通过客户端程序访问 Cookie。
    

httpOnly 是微软对 Cookie 做的扩展。如果在 Cookie 中设置了 httpOnly 属性,则通过程序(JS 脚本、Applet 等)将无法读取到 Cookie 信息,防止 XSS 攻击产生。

服务器的 IP 配置了多个域名解析,此时设置 Cookie 就需要配置多个生效域。

例如,服务器的 IP 配置了域名泛解析 *.linner.com

c.SetCookie(key, value, 3600, "/", ".linner.com", false, false)

Session

Gin 是一个轻量的 Web 框架,在 Gin 中并不直接对 Session 提供支持,但是可以通过其他第三方中间件让 Gin 支持 Session。

在使用 Session 前,需要 get 对应的模块:

go get github.com/gin-contrib/sessions

然后在需要使用到 Session 的地方引入:

import "github.com/gin-contrib/sessions"

接着设置 Session 中间件:

// 创建基于 Cookie 的存储引擎,secret 参数是加密密钥
store := cookie.NewStore([]byte("secret"))
// 配置 Session 中间件
r.Use(sessions.Sessions("mysession", store))

具体示例如下:

func SetSession(c *gin.Context) {
	// 从 Context 中获取 Session 数据
	session := sessions.Default(c)
	// 设置 Session
	session.Set("username", "lisi")
	session.Set("nickname", "李四")

	// 保存设置的 Session,设置完成之后必须要调用 Save()
	if err := session.Save(); err != nil {
		logrus.Error(err)
	}

	c.Status(http.StatusOK)
}

func GetSession(c *gin.Context) {
	// 从 Context 中获取 Session 数据
	session := sessions.Default(c)
	// 获取 Session
	username := session.Get("username")
	nickname := session.Get("nickname")

	c.JSON(http.StatusOK, gin.H{
		"code": http.StatusOK,
		"data": gin.H{
			"username": username,
			"nickname": nickname,
		},
	})
}

var store = cookie.NewStore([]byte("secret"))

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

	// 设置 Session 中间件
	r.Use(sessions.Sessions("mysession", store))

	r.POST("/session", SetSession)
	r.GET("/session", GetSession)

	if err := r.Run(":8080"); err != nil {
		logrus.Error(err)
	}
}

Redis

github.com/gin-contrib/sessions 支持通过 Redis 缓存 Session,使其能够支持分布式系统。

在使用前需要先引入其 Redis 包:

$ go get -u github.com/gin-contrib/sessions/redis

接着配置 Redis Store:

// 配置 Redis Store
func RedisStore() (store sessions.Store) {
	if store, err := redis.NewStore(10, "tcp",
		"localhost:6379", "", "123456", []byte("secret")); err != nil {
		logrus.Error(err)
		panic(err)
	} else {
		return store
	}
}

var store = RedisStore()

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

	// 设置 Session 中间件
	r.Use(sessions.Sessions("mysession", store))

	r.POST("/session", SetSession)
	r.GET("/session", GetSession)

	if err := r.Run(":8080"); err != nil {
		logrus.Error(err)
	}
}

redis.NewStore() 的定义如下:

func NewStore(size int, network, address, username, password string, keyPairs ...[]byte) (Store, error) {
	// ...
}

其中:

  • size:最大连接数;
  • network:连接方式,值为 tcpudp
  • address:服务器地址,需要指定端口号;
  • username:Redis 用户名,没有配置用户名可传入空字符串;
  • password:Redis 密码,没有配置密码可传入空字符串;
  • keyPairs:Session 加密密钥。