之前的文章都是一种无状态的请求,在处理有状态的请求时,如用户登录的场景就不行了,web系统通常使用cookie或者session来记录用户状态,本文记录一下gin框架下cookie与session的使用。

读取cookie

gin.Context 里有个Cookie 方法,返回想要获取的cookie,如果不存在,则会返回一个错误 http: named cookie not present ,可以通过判断是否有这个错误来判断是否有相关的cookie。

1
2
3
4
5
6
7
8
func (ApiV1) Cookies(c *gin.Context) {
	cookie, err := c.Cookie("user")
	if err != nil{
		c.JSON(200, gin.H{"msg": err.Error()})
	}else{
		c.JSON(200, gin.H{"msg": cookie})
	}
}

设置cookie

可以使用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 就会失效,它就不再是我们有效的 cookie,他的时间单位是秒second;
  • 第四个参数path 为 cookie 所在的目录;
  • 第五个domain 为所在域,表示我们的 cookie 作用范围,里面可以是localhost也可以是你的域名,看自己情况;
  • 第六个secure 表示是否只能通过 https 访问,为true只能是https;
  • 第七个httpOnly 表示 cookie 是否可以通过 js代码进行操作,为true时不能被js获取
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func (ApiV1) SetCookie(c *gin.Context)  {
	cookie, err := c.Cookie("user")
	if err != nil{
		c.SetCookie("user", "yangyanxing",3600, "/", "127.0.0.1",
			false, false)
		c.JSON(200, gin.H{"msg": "设置cookie成功"})
	}else{
		c.JSON(200, gin.H{"msg": cookie})
	}
}

删除cookie

没有一个方法可以直接删除cookie,但是可以通过设置有效期为负数来达到删除的目的

比如要删除掉user这个cookie,其实这时候它的值是多少已经无所谓了,可以设置个空

1
2
3
4
func (ApiV1) DelCookie(c *gin.Context)  {
	c.SetCookie("user", "", -1,"/", "127.0.01", false, false)
	c.String(200,"删除user cookie成功")
}

操作session

cookie 虽然很方便,但是它的安全性还是有些问题,客户端可以任意的做修改,这时也可以使用session 来存储状态。gin 框架本身不支持session,但是我们可以使用第三方库 https://github.com/gin-contrib/sessions 来操作session,这个session框架可以将信息存储在不同的地方

gin-contrib/sessions中间件支持的存储引擎:

  • cookie
  • memstore
  • redis
  • memcached
  • mongodb

使用cookie存储

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	// 创建基于cookie的存储引擎,yangyanxing 参数是用于加密的密钥
	store := cookie.NewStore([]byte("yangyanxing"))
	// 设置session中间件,参数mysession,指的是session的名字,也是cookie的名字
	// store是前面创建的存储引擎,我们可以替换成其他存储引擎
	r.Use(sessions.Sessions("mysession", store))
	apiv1_h := handlers.ApiV1{}
	apiv1 := r.Group("/api/v1")
	{
		apiv1.GET("/session/get", apiv1_h.GetSession)
		apiv1.GET("/session/set", apiv1_h.SetSession)

	}
	r.Run(":8080")
}

store := cookie.NewStore([]byte("yangyanxing")) 这行代码初始化一个存储引擎,参数为密钥,用于加解密

r.Use(sessions.Sessions("mysession", store)) 这是一个中间件,cookie就是以mysesson为name存储,store 为上面初始化的存储引擎。

在处理类中操作session

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func (ApiV1) GetSession(c *gin.Context)  {
	session := sessions.Default(c)
	if value:= session.Get("user"); value==nil{
		c.String(200,"在session中没有user")
	}else{
		c.String(200, value.(string))
	}
}

func (receiver ApiV1) SetSession(c *gin.Context)  {
	session := sessions.Default(c)
	session.Set("user", "yangyanxing")
	err := session.Save()
	if err != nil {
		c.String(200, err.Error())
	}
	c.String(200, "设置session成功")
}

注意,对session进行写操作,如设置修改删除等,必须要调用session.Save() 方法,否则不能生效。

下图为在chrome中查看到的cookies信息。可以看到,value 是加密的

image-20220105224736107

也可以一次性全部清除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func (ApiV1) ClearSession(c *gin.Context)  {
	session := sessions.Default(c)
	//session.Delete("user")
	session.Clear()
	err := session.Save()
	if err != nil {
		c.String(200, err.Error())
		return
	}
	c.String(200, "清理成功")
}

Delete 为删除单个值,Clear 为清除所有,但是注意,即使使用了Clear,它只是清除了session中的信息,但是cookie中的mysession还是存在的,但是值是不一样了。

使用redis 存储 session

用法和cookie 一样,只是在初始化存储引擎的时候不一样

store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []**byte**("secret"))

参数说明

  • 第1个参数 - redis最大的空闲连接数
  • 个参数 - 数通信协议tcp或者udp
  • 参数 - redis地址, 格式,host:port
  • 第4个参数 - redis密码
  • 第5个参数 - session加密密钥

注意:

使用 redis.NewStore 初始化的store 默认使用redis的db0, 更多的时候,我们会使用redis.NewRediStoreWithDB 来指定db

参考文章

golang gin setcookie参数详解

https://github.com/gin-contrib/sessions

Gin框架如何处理session