Cookie

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

 1func SetCookie(c *gin.Context) {
 2	// 设置 Cookie
 3	c.SetCookie("gin-cookie", "test", 3600, "/", "localhost", false, false)
 4	c.JSON(http.StatusOK, gin.H{
 5		"code": http.StatusOK,
 6		"data": value,
 7	})
 8}
 9func GetCookie(c *gin.Context) {
10	// 获取 Cookie
11	if value, err := c.Cookie(key); err != nil {
12		c.Status(http.StatusBadRequest)
13	} else {
14		c.JSON(http.StatusOK, gin.H{
15			"code": http.StatusOK,
16			"data": value,
17		})
18	}
19}
20
21func main() {
22	r := gin.Default()
23
24	r.POST("/cookie", SetCookie)
25	r.GET("/cookie", GetCookie)
26
27	if err := r.Run(":8080"); err != nil {
28		logrus.Error(err)
29	}
30}

其中:

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

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

    1func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
    2	// ...
    3}
    

    参数解释如下:

    • 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

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

Session

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

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

1go get github.com/gin-contrib/sessions

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

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

接着设置 Session 中间件:

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

具体示例如下:

 1func SetSession(c *gin.Context) {
 2	// 从 Context 中获取 Session 数据
 3	session := sessions.Default(c)
 4	// 设置 Session
 5	session.Set("username", "lisi")
 6	session.Set("nickname", "李四")
 7
 8	// 保存设置的 Session,设置完成之后必须要调用 Save()
 9	if err := session.Save(); err != nil {
10		logrus.Error(err)
11	}
12
13	c.Status(http.StatusOK)
14}
15
16func GetSession(c *gin.Context) {
17	// 从 Context 中获取 Session 数据
18	session := sessions.Default(c)
19	// 获取 Session
20	username := session.Get("username")
21	nickname := session.Get("nickname")
22
23	c.JSON(http.StatusOK, gin.H{
24		"code": http.StatusOK,
25		"data": gin.H{
26			"username": username,
27			"nickname": nickname,
28		},
29	})
30}
31
32var store = cookie.NewStore([]byte("secret"))
33
34func main() {
35	r := gin.Default()
36
37	// 设置 Session 中间件
38	r.Use(sessions.Sessions("mysession", store))
39
40	r.POST("/session", SetSession)
41	r.GET("/session", GetSession)
42
43	if err := r.Run(":8080"); err != nil {
44		logrus.Error(err)
45	}
46}

Redis

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

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

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

接着配置 Redis Store:

 1// 配置 Redis Store
 2func RedisStore() (store sessions.Store) {
 3	if store, err := redis.NewStore(10, "tcp",
 4		"localhost:6379", "", "123456", []byte("secret")); err != nil {
 5		logrus.Error(err)
 6		panic(err)
 7	} else {
 8		return store
 9	}
10}
11
12var store = RedisStore()
13
14func main() {
15	r := gin.Default()
16
17	// 设置 Session 中间件
18	r.Use(sessions.Sessions("mysession", store))
19
20	r.POST("/session", SetSession)
21	r.GET("/session", GetSession)
22
23	if err := r.Run(":8080"); err != nil {
24		logrus.Error(err)
25	}
26}

redis.NewStore() 的定义如下:

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

其中:

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