相关推荐recommended
Gin框架: Cookie和Session在单体架构和分布式架构下的应用
作者:mmseoamin日期:2024-02-22

Gin 中单一Cookie的应用

1 )路由处理

package routers
import (
	"gin-demo/controllers/web"
	"github.com/gin-gonic/gin"
)
func WebRoutersInit(r *gin.Engine) {
	webRouters := r.Group("/")
	{
		webRouters.GET("/", web.WebCtrl{}.Index)
		webRouters.GET("/setcookie", web.WebCtrl{}.SetCookie)
		webRouters.GET("/getcookie", web.WebCtrl{}.GetCookie)
		webRouters.GET("/delcookie", web.WebCtrl{}.DelCookie)
	}
}

2 ) 控制器处理

package web
import (
	"net/http"
	"github.com/gin-gonic/gin"
)
type WebCtrl struct{}
// 设置 cookie
func (con WebCtrl) SetCookie(c *gin.Context) {
	// 设置 cookie
	// 第一个
	c.SetCookie("username", "张三", 3600, "/", "localhost", false, true)
	// 第二个
	c.SetCookie("hobby", "吃饭 睡觉", 5, "/", "localhost", false, true)
	// 响应
	c.String(http.StatusOK, "set cookie")
}
// 获取 cookie
func (con WebCtrl) GetCookie(c *gin.Context) {
	// 获取 cookie
	username, _ := c.Cookie("username")
	hobby, _ := c.Cookie("hobby")
	// 响应
	c.String(http.StatusOK, "username=%v----hobby=%v", username, hobby)
}
// 删除 cookie
func (con WebCtrl) DelCookie(c *gin.Context) {
	// 删除 cookie
	c.SetCookie("username", "张三", -1, "/", "localhost", false, true) // 删除一个
	// 响应
	c.String(http.StatusOK, "delete cookie")
}
  • 设置cookie时,设置了两个不同过期时间的cookie

  • 5s 后第一个cookie 自动丢失

  • 访问 /delcookie 路由,第二个路由被主动删除

  • HTTP 是无状态协议,当你浏览了一个页面

  • 然后转到同一个网站的另一个页面,服务器无法认识到这是同一个浏览器在访问同一个网站

  • 每一次的访问,都是没有任何关系的

  • 如果我们要实现多个页面之间共享数据的话

  • 我们就可以使用 Cookie 或者 Session 实现

  • cookie 是存储于访问者计算机的浏览器中

  • 可以让我们用同一个浏览器访问同一个域名的时候共享数据

  • 所以,cookie的功能

    • 保持用户登录状态
    • 保存用户浏览的历史记录
    • 猜你喜欢,智能推荐
    • 电商网站的加入购物车
    • cookie的文档

      • https://gin-gonic.com/zh-cn/docs/examples/cookie/
      • 设置cookie时的API

        • c.SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
        • 第一个参数 key
        • 第二个参数 value
        • 第三个参数 过期时间
          • 如果只想设置 Cookie 的保存路径而不想设置存活时间
          • 可以在第三个参数中传递 nil
          • 第四个参数 cookie 的路径
          • 第五个参数 cookie 的路径 Domain 作用域
            • 本地调试配置成 localhost , 正式上线配置成域名
            • 第六个参数是 secure
              • 当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效
              • 第七个参数 httpOnly
                • 是微软对 COOKIE 做的扩展, 如果在 COOKIE 中设置了“httpOnly”属性
                • 则通过程序(JS 脚本、applet 等)将无法读取到 COOKIE 信息,防止 XSS 攻击产生

                  多级域名共享 cookie

                  • 不管二级或多级域名,可以设置根域名来共享数据
                  • 分别把 a.xyz.com 和 b.xyz.com, 以及 c.b.xyz.com 解析到我们的服务器
                  • 我们想的是用户在 a.xyz.com 中设置 Cookie 信息后
                  • 在 b.xyz.com 以及 c.b.xyz.com 中获取刚才设置的cookie
                  • 也就是实现多个二级域名共享 cookie,这时候就可以这样设置 cookie
                  • c.SetCookie("usrename", "张三", 3600, "/", ".xyz.com", false, true)

                    单体架构 基于 cookie 存储 session

                    概述

                    1 ) 单单使用 cookie 的限制

                    • 单单基于 cookie 的设定,只能读取单个客户端中的数据,不能做到数据在服务端共享
                    • 服务端Cookie技术是将数据存储在用户的浏览器上,每次浏览器发送请求时
                    • 都会携带这些Cookie数据发送到服务器。Cookie的大小通常受到限制
                    • 大多数浏览器对单个Cookie的大小有4096字节的限制
                    • 尽管现在一些新的浏览器和客户端设备支持更大的Cookie
                    • 此外,用户可以选择禁用Cookie功能,这会影响Cookie的正常使用

                      2 )基于 cookie 的 session 技术

                      • 与Cookie不同,Session技术将数据存储在服务器上
                      • 这意味着Session没有存储大小的限制,只受限于服务器的内存大小
                      • Session在用户访问期间一直存在在服务器上,直到会话结束或服务器决定删除它
                      • 由于Session存储在服务器上,因此相比Cookie,它更安全,不容易被篡改
                      • 但是,如果服务器上有大量的Session,会占用较多的服务器资源
                      • 可能会影响服务器的性能

                        示例

                        1 )主程序配置 在 main.go 中

                        package main
                        import (
                        	"github.com/gin-contrib/sessions"
                        	"github.com/gin-contrib/sessions/cookie"
                        	"github.com/gin-gonic/gin"
                        )
                        func main() {
                        	// 创建一个默认的路由引擎
                        	r := gin.Default()
                        	
                        	// ... 跳过其他
                        	
                        	// 在路由配置之上,配置session中间件
                        	cookieSessionSecret := "sdfdssdsfs_s?d2sdfsf@^s_" // 是用于加密的密钥
                        	// 创建基于 cookie 的存储引擎
                        	store := cookie.NewStore([]byte(cookieSessionSecret))
                        	// 配置session的中间件 store是前面创建的存储引擎,我们可以替换成其他存储引擎
                        	r.Use(sessions.Sessions("xxproject_session", store))
                        	
                        	// ... 跳过其他
                        }
                        

                        2 ) 路由配置

                        package routers
                        import (
                        	"gin-demo/controllers/web"
                        	"github.com/gin-gonic/gin"
                        )
                        func WebRoutersInit(r *gin.Engine) {
                        	webRouters := r.Group("/")
                        	{
                        		webRouters.GET("/", web.WebCtrl{}.Index)
                        		webRouters.GET("/setsession", web.WebCtrl{}.SetSession)
                        		webRouters.GET("/getsession", web.WebCtrl{}.GetSession)
                        		webRouters.GET("/delsession", web.WebCtrl{}.DelSession)
                        	}
                        }
                        

                        3 ) controller 控制器

                        package web
                        import (
                        	"net/http"
                        	"github.com/gin-contrib/sessions" // 则个是用于 直接获取 cookie 内容的
                        	"github.com/gin-gonic/gin"
                        )
                        type WebCtrl struct{}
                        // 设置 session
                        func (con WebCtrl) SetSession(c *gin.Context) {
                        	session := sessions.Default(c)
                        	// 配置session的过期时间
                        	session.Options(sessions.Options{
                        		// MaxAge: 3600 * 6, // 6hrs   MaxAge单位是秒
                        		MaxAge: 5, // 5 s 过期
                        	})
                        	session.Set("username", "张三 111")
                        	session.Save() // 设置session的时候必须调用
                        	// 响应
                        	c.String(http.StatusOK, "set session: %v", session.Get("username"))
                        }
                        // 获取 session
                        func (con WebCtrl) GetSession(c *gin.Context) {
                        	session := sessions.Default(c)
                        	username := session.Get("username")
                        	// 响应
                        	c.String(http.StatusOK, "get session username=%v", username)
                        }
                        // 删除 session
                        func (con WebCtrl) DelSession(c *gin.Context) {
                        	session := sessions.Default(c)
                        	session.Delete("username")
                        	session.Save() // 这将从 Cookie中 中删除 session 数据
                        	username := session.Get("username") // 获取 session username
                        	// 响应
                        	c.String(http.StatusOK, "delete session %v", username)
                        }
                        
                        • 基于cookie的 session 技术如上是基本使用程序
                        • 需要注意的是,关于 session 删除同步到 cookie 删除是自动设置的
                        • 调用 session 的 Delete 和 Save 方法来更新 Cookie
                        • 由于 session 数据已经被删除,这个操作会创建一个新的、空的 Cookie
                        • 这实际上会导致浏览器端的旧 Cookie 过期并被删除,所以无需我们控制cookie

                          分布式架构下 基于 redis 存储 session

                          概述

                          • 如果项目部署在多台机器上,单体架构下的session无法共享,需要借助第三方来同步数据
                          • 这时候就会用到 redis 或其他服务器存储技术,在负载均衡下的多台机器共享数据的场景
                          • 就是分布式架构下的 session 应用场景

                            示例

                            1 )主程序 main.go

                            package main
                            import (
                            	"github.com/gin-contrib/sessions"
                            	"github.com/gin-contrib/sessions/redis"
                            	"github.com/gin-gonic/gin"
                            )
                            func main() {
                            	// 创建一个默认的路由引擎
                            	r := gin.Default()
                            	
                            	// ... 跳过其他
                            	
                            	// 在路由配置之上,配置session中间件
                            	redisSessionSecret := "sdfdssdsfs_s?d2sdfsf@^s_" // 是用于加密的密钥
                            	// 配置session中间件
                            	
                            	store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte(redisSessionSecret))
                            	r.Use(sessions.Sessions("mysession", store))
                            	// ... 跳过其他
                            }
                            

                            2 ) 路由和控制器同上面的 cookie based session 示例,这里不再赘述

                            3 )注意

                            • 初始化基于 redis 的session存储引擎, 参数说明:
                              • 第 1 个参数 - redis 最大的空闲连接数
                              • 第 2 个参数 - 数通信协议 tcp 或者 udp
                              • 第 3 个参数 - redis 地址, 格式,host:port
                              • 第 4 个参数 - redis 密码
                              • 第 5 个参数 - session 加密密钥
                              • 同步删除 session 的时候,redis 的数据也会被同步删除,同时浏览器的Cookie数据也会同步消失
                              • 有时候需要手动删除,比如客户端调用退出登录接口的时候,手动调用 session的删除和保存方法

                                其他

                                • 关于多 session 和 其他存储引擎的 session 都在文档中
                                • 参考:https://github.com/gin-contrib/sessions