gin会话控制

一. 会话控制

1.1 介绍

  • HTTP是无状态的协议,不会记录用户的任何信息,服务器也不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否是同一个客户端发出的。
  • Cookie是解决HTTP协议无状态的方案之一
  • Cookie实际就是服务器保存在浏览器上的一段信息。浏览器有了Cookie之后,每次向服务器发送请求时,都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求。
  • Cookie由服务器创建,并发送给浏览器,最终由浏览器保存。

1.2 Cookie用途

  • 服务器发送Cookie给客户端,客户端发送请求时携带Cookie

1.3 Cookie使用

  • gin.Context的SetCookie方法可以用来设置Cookie
  • SetCookie将Set-Cookie表头添加到ResponseWrite的标头中。提供的cookie必须有一个有效的名称,无效的cookie可能会被无提示删除。
Go 复制代码
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
	if path == "" {
		path = "/"
	}
	http.SetCookie(c.Writer, &http.Cookie{
		Name:     name,
		Value:    url.QueryEscape(value),
		MaxAge:   maxAge,
		Path:     path,
		Domain:   domain,
		SameSite: c.sameSite,
		Secure:   secure,
		HttpOnly: httpOnly,
	})
}
  • 参数
    • name:cookie的key
    • value:cookie的value
    • maxAge:过期时间,如果只想设置Cookie的保存路径而不想设置存活时间,可以在第三个参数中传nil
    • path:cookie的路径
    • domain:cookie路径作用域,本地调试配置成localhost,正式上线配成域名
    • secure:为true时,cookie在HTTP中无效,在HTTPS中有效
    • httpOnly:是微软对 COOKIE 做的扩展。如果在 COOKIE 中设置了 "httpOnly" 属性,则通过程序(JS 脚本、 applet 等)将无法读取到 COOKIE 信息,防止 XSS 攻击产生。
  • 获取Cookie,通过gin.Context的Cookie方法
Go 复制代码
package main

import (
	"fmt"

	"github.com/gin-gonic/gin"
)

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

	r.GET("cookie", func(c *gin.Context) {
		//获取客户端是否携带cookie
		cookie, err := c.Cookie("key_cookie")
		if err != nil {
			cookie = "NoCookie"
			//给客户端设置cookie
			//maxAge 单位为秒
			c.SetCookie("key_cookie", "val_cookie", 60, "/", "localhost", false, true)
		}
		fmt.Println("cookie: ", cookie)
	})
	r.Run()
}

1.4 Cookie练习

  • 模拟实现权限验证中间件
    • 两个路由login和home
    • login用于设置cookie
    • home处理请求返回响应
    • 在请求home前,先跑中间件代码,检验是否存在cookie
Go 复制代码
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func MiddleWare() gin.HandlerFunc {
	return func(c *gin.Context) {
		//获取cookie
		cookie, err := c.Cookie("key_cookie")
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "fail", "err": err.Error()})
			//验证不通过,不在调用后续函数处理
			c.Abort()
			return
		}
		if cookie != "val_cookie" {
			c.JSON(http.StatusBadRequest, gin.H{"status": "fail", "err": "err cookie"})
			//验证不通过,不在调用后续函数处理
			c.Abort()
			return
		}
		//执行路由处理代码
		c.Next()
	}
}

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

	r.GET("/login", func(c *gin.Context) {
		//设置cookie
		c.SetCookie("key_cookie", "val_cookie", 60, "/", "127.0.0.1", false, true)
		c.String(http.StatusOK, "Login success")
	})

	r.GET("/home", MiddleWare(), func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"data": "home"})
	})
	r.Run()
}

演示:

1.5 Cookie缺点

  • 不安全,明文发送
  • 增加带宽消耗
  • 可以被禁用
  • cookie有上限

1.6 Session

在web开发中,大家一定会使用到session。在go框架中并没有集成session管理的中间件。要想使用session功能。可以使用gorilla/sessions这个包。

1.6.1 什么是session

session就是用来在服务端存储相关数据的,以便在同一个用户多次请求之间保存用户的状态,比如登录状态。因为HTTP协议是无状态的,不记录用户的信息,使用cookie的缺点不安全,明文发送,而session通过使用标识,也就是所谓的sessionid来解决cookie的不安全的缺点。

该sessionid有服务器生成,并存储在客户端的cookie或者url中。当客户端再次发送请求时,会携带该标识,服务端可以通过该标识查找到存在服务器上的相关数据。

原理如下:

1.6.2 gorilla/sessions包

gorilla/sessions包提供了将session数据存储与cookie和文件中的功能。同时还支持自定义后端存储,比如将session数据存储与redis,mysql等等。

使用下面命令安装:

go get github.com/gorilla/sessions

1.6.3 基本使用

该包的使用可以分为五步:定义存储session的变量,程序启动时实例化具体的session存储类型,读取或存储数据到session,持久化session。

Go 复制代码
package main

import (
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/sessions"
)

// 第一步:定义全局的存储session数据变量
var sessionStore sessions.Store

func main() {
	r := gin.Default()
	//第二步:程序启动后,指定具体的存储数据类型:redis,mysql或文件
	//这里指定的是文件
	sessionStore = sessions.NewFilesystemStore(".", []byte("Hello"))
	r.GET("/save", func(c *gin.Context) {
		//第三步:在具体的handler中获取session
		//Get() 总是返回一个session
		session, err := sessionStore.Get(c.Request, "sessionid")
		if err != nil {
			log.Println(err.Error())
		}
		//第四步:从session中存储或取出数据
		session.Values["userid"] = "123123"
		//第五步保存session数据,本质上是将内存中的数据持久化到存储介质中
		//本例是持久化到文件中
		session.Save(c.Request, c.Writer)
		c.JSON(http.StatusOK, gin.H{
			"status": "ok",
		})
	})

	r.GET("/get", func(c *gin.Context) {
		session, err := sessionStore.Get(c.Request, "sessionid")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"status": "false",
				"err":    err.Error(),
			})
		}

		userid := session.Values["userid"]
		c.JSON(http.StatusOK, gin.H{
			"message": userid,
			"status":  "ok",
		})
	})
	r.Run()
}

在该示例中,第一步的sessions.Store本质是一个接口类型,只要实现了该接口,就可以存储session数据。在第二步中就指定了具体存储类型:文件类型。返回的session的类型为*sessions.FilesystemStore,实现了session.Store接口。

在第三步获取session时,Store.Get有两个参数,一个是请求参数request,一个是session-name,存储于cookie或url中,当然也可以是在Header中。服务端从request中通过该参数名获取session-id,在通过session-id从后端存储中(文件,redis或mysql等)获取对应的数据,则读取出来并解析到session对象中,否则就初始化一个新的session对象。

第五步本质上是持久化。因为在第四步只是将数据保存到内存中,需要调用Save才能持久化到对应的存储介质。

演示:

目录结构:

客户端:

服务器多出来的session文件内容是一个加密字符串。

1.6.4 实现原理

session的存储本质上就是在服务端给每一个用户存储一行记录。服务端给每个用户分配一个唯一的session-id,以session-id为主键,存储对应的值。如果存储在mysql中,sessioin-id就是主键;如果存储在redis中,session-id就是key;如果存储在文件中,session-id就是对应的文件名,文件内容就是存储的session数据。

cookie和session原理:

我们拿登录账号举例:

我们知道http协议是无状态的,并不会记录用户的信息。但是我们实际生活中使用网页需要登录时,一般我们登录了一次,之后并不会需要再登录了。

这是因为http本身是无状态的,但是如果访问某资源频繁需要登录,会导致用户的体验不好。

cookie原理:

于是当浏览器访问某资源需要登录时,客户端将name和passwd发送个服务器,服务器与注册时保存的name和passwd对比,成功后响应,将name和passwd放在报头的set-cookie中发送给客户端,客户端将其保存到cookie中,下次访问时,将cookie信息发送给服务器,就不需要登录了。

cookie本质是一个浏览器的文件,里面保存用户的一些信息。

文件保存分为内存级(关闭浏览器进程就空间释放了),硬盘级文件(长时间保存在硬盘中,关闭进程并不会释放)。

但是:直接发送cookie,cookie保存的name和passwd并不安全,中间人可以直接获得用户的name和passward。

session原理:

在本地保存用户的信息,发送的时候不安全。于是,在客户端登录时,将name和passwd发送给服务器,服务器形成一个文件session,里面有一个标识sid,全互联网唯一,标识的是用户的信息,响应时将sid放到set-cookie里发送给客户端,客户端将sid保存到cookie中,下一次访问带有的cookie中保存的是sid,服务器只要拿着sid比较即可。

这样用户信息保存到了服务器,相比较只使用cookie安全性会高一点。

相关推荐
RationalDysaniaer1 天前
Gin入门笔记
笔记·gin
千年死缓1 天前
gin中间件
中间件·gin
codists5 天前
《使用Gin框架构建分布式应用》阅读笔记:p393-p437
golang·gin·编程人
景天科技苑5 天前
【Golang】Gin框架中如何使用JWT来实现登录认证
开发语言·golang·gin·jwt·登录认证·go jwt
产幻少年7 天前
gin框架可以构建微服务吗?
微服务·gin
get2007 天前
golang gin ShouldBind的介绍和使用
开发语言·golang·gin
codists8 天前
《使用Gin框架构建分布式应用》阅读笔记:p272-p306
后端·go·gin
codists8 天前
《使用Gin框架构建分布式应用》阅读笔记:p212-p233
笔记·golang·gin·编程人·codists·gin框架
knoci9 天前
【Go】-基于Gin框架的博客项目
后端·学习·golang·gin
codists10 天前
《使用Gin框架构建分布式应用》阅读笔记:p251-p271
golang·gin·编程人