Cookie 和 Session

HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性。

Session 是什么

客户端请求服务端,服务端会为这次请求开辟一块内存空间,这个对象便是 Session 对象,存储结构为 ConcurrentHashMap。Session 弥补了 HTTP 无状态特性,服务器可以利用 Session 存储客户端在同一个会话期间的一些操作记录。

Session 如何判断是否是同一会话

服务器第一次接收到请求时,开辟了一块 Session 空间(创建了Session对象),同时生成一个 sessionId ,并通过响应头的 Set-Cookie:JSESSIONID=XXXXXXX 命令,向客户端发送要求设置 Cookie 的响应;客户端收到响应后,在本机客户端设置了一个 JSESSIONID=XXXXXXX 的 Cookie 信息,该 Cookie 的过期时间为浏览器会话结束。
第八章 会话保持 - 图1
接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie 信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId。

Session 的缺点

Session 机制有个缺点,比如 A 服务器存储了 Session,就是做了负载均衡后,假如一段时间内 A 的访问量激增,会转发到 B 进行访问,但是 B 服务器并没有存储 A 的 Session,会导致 Session 的失效。

Cookies 是什么

HTTP 协议中的 Cookie 包括 Web Cookie浏览器 Cookie,它是服务器发送到 Web 浏览器的一小块数据。服务器发送到浏览器的 Cookie,浏览器会进行存储,并与下一个请求一起发送到服务器。通常,它用于判断两个请求是否来自于同一个浏览器,例如用户保持登录状态。

HTTP Cookie 机制是 HTTP 协议无状态的一种补充和改良

Cookie 主要用于下面三个目的

  • 会话管理

登陆、购物车、游戏得分或者服务器应该记住的其他内容

  • 个性化

用户偏好、主题或者其他设置

  • 追踪

记录和分析用户行为
Cookie 曾经用于一般的客户端存储。虽然这是合法的,因为它们是在客户端上存储数据的唯一方法,但如今建议使用现代存储 API。Cookie 随每个请求一起发送,因此它们可能会降低性能(尤其是对于移动数据连接而言)。

创建 Cookie

当接收到客户端发出的 HTTP 请求时,服务器可以发送带有响应的 Set-Cookie 标头,Cookie 通常由浏览器存储,然后将 Cookie 与 HTTP 标头一同向服务器发出请求。

Set-Cookie 和 Cookie 标头

Set-Cookie HTTP 响应标头将 cookie 从服务器发送到用户代理。下面是一个发送 Cookie 的例子
第八章 会话保持 - 图2
此标头告诉客户端存储 Cookie
现在,随着对服务器的每个新请求,浏览器将使用 Cookie 头将所有以前存储的 Cookie 发送回服务器。
第八章 会话保持 - 图3
有两种类型的 Cookies,一种是 Session Cookies,一种是 Persistent Cookies,如果 Cookie 不包含到期日期,则将其视为会话 Cookie。会话 Cookie 存储在内存中,永远不会写入磁盘,当浏览器关闭时,此后 Cookie 将永久丢失。如果 Cookie 包含有效期 ,则将其视为持久性 Cookie。在到期指定的日期,Cookie 将从磁盘中删除。
还有一种是 Cookie的 Secure 和 HttpOnly 标记,下面依次来介绍一下

会话 Cookies

上面的示例创建的是会话 Cookie ,会话 Cookie 有个特征,客户端关闭时 Cookie 会删除,因为它没有指定ExpiresMax-Age 指令。
但是,Web 浏览器可能会使用会话还原,这会使大多数会话 Cookie 保持永久状态,就像从未关闭过浏览器一样。

永久性 Cookies

永久性 Cookie 不会在客户端关闭时过期,而是在特定日期(Expires)特定时间长度(Max-Age)外过期。例如

  1. Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

Cookie的 Secure 和 HttpOnly 标记

安全的 Cookie 需要经过 HTTPS 协议通过加密的方式发送到服务器。即使是安全的,也不应该将敏感信息存储在cookie 中,因为它们本质上是不安全的,并且此标志不能提供真正的保护。
HttpOnly 的作用

  • 会话 Cookie 中缺少 HttpOnly 属性会导致攻击者可以通过程序(JS脚本、Applet等)获取到用户的 Cookie 信息,造成用户 Cookie 信息泄露,增加攻击者的跨站脚本攻击威胁。
  • HttpOnly 是微软对 Cookie 做的扩展,该值指定 Cookie 是否可通过客户端脚本访问。
  • 如果在 Cookie 中没有设置 HttpOnly 属性为 true,可能导致 Cookie 被窃取。窃取的 Cookie 可以包含标识站点用户的敏感信息,如 ASP.NET 会话 ID 或 Forms 身份验证票证,攻击者可以重播窃取的 Cookie,以便伪装成用户或获取敏感信息,进行跨站脚本攻击等。

    Cookie 的作用域

    DomainPath 标识定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。
    Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前主机(不包含子域名)。如果指定了Domain,则一般包含子域名。
    例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如developer.mozilla.org)。
    例如,设置 Path=/docs,则以下地址都会匹配:

  • /docs

  • /docs/Web/
  • /docs/Web/HTTP

Go操作session和cookie

session

在Gin框架中,我们可以依赖gin-contrib/sessions中间件处理session。
gin-contrib/sessions中间件支持的存储引擎:

  • cookie
  • memstore
  • redis
  • memcached
  • mongodb

下面介绍session的用法

1.安装session包

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

2.session的基本用法

  1. package main
  2. import (
  3. "github.com/gin-contrib/sessions"
  4. "github.com/gin-contrib/sessions/cookie"
  5. "github.com/gin-gonic/gin"
  6. )
  7. func main() {
  8. // 1、创建路由
  9. g := gin.Default()
  10. // 2、创建基于cookie的存储引擎,其中参数是用于加密的密钥
  11. newStore := cookie.NewStore([]byte("joker@123456"))
  12. // 3、设置session中间件,参数mysession,指的是session的名字,也是cookie的名字
  13. // store是前面创建的存储引擎,我们可以替换成其他存储引擎
  14. g.Use(sessions.Sessions("mysession",newStore))
  15. // 4、绑定路由规则,执行视图函数
  16. g.GET("/user", func(c *gin.Context) {
  17. // 初始化session对象
  18. session := sessions.Default(c)
  19. // 通过session.Get()获取session的值
  20. if session.Get("name") != "joker"{
  21. // 设置session值
  22. session.Set("name","joker")
  23. // 删除session值
  24. session.Delete("joker")
  25. // 保存session值
  26. _ = session.Save()
  27. // 清楚整个session
  28. //session.Clear()
  29. }
  30. c.JSON(200,gin.H{
  31. "name":session.Get("name"),
  32. })
  33. })
  34. // 4、开启监听
  35. g.Run(":8000")
  36. }

3.基于redis存储引擎的session

  1. package main
  2. import (
  3. "github.com/gin-contrib/sessions"
  4. "github.com/gin-contrib/sessions/redis"
  5. "github.com/gin-gonic/gin"
  6. )
  7. func main() {
  8. r := gin.Default()
  9. // 初始化基于redis的存储引擎
  10. // 参数说明:
  11. // 第1个参数 - redis最大的空闲连接数
  12. // 第2个参数 - 数通信协议tcp或者udp
  13. // 第3个参数 - redis地址, 格式,host:port
  14. // 第4个参数 - redis密码
  15. // 第5个参数 - session加密密钥
  16. store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))
  17. r.Use(sessions.Sessions("mysession", store))
  18. r.GET("/incr", func(c *gin.Context) {
  19. session := sessions.Default(c)
  20. var count int
  21. v := session.Get("count")
  22. if v == nil {
  23. count = 0
  24. } else {
  25. count = v.(int)
  26. count++
  27. }
  28. session.Set("count", count)
  29. session.Save()
  30. c.JSON(200, gin.H{"count": count})
  31. })
  32. r.Run(":8000")
  33. }

cookie

cookie通常用于在浏览器中保存一些小数据,例如客户标识、用户非铭感数据。注意别使用cookie保存隐私数据。
gin框架主要通过上下文对象提供的SetCookie和Cookie两个函数操作cookie

1.设置cookie

例子:

  1. import (
  2. "fmt"
  3. "github.com/gin-gonic/gin"
  4. )
  5. func main() {
  6. router := gin.Default()
  7. router.GET("/cookie", func(c *gin.Context) {
  8. // 设置cookie
  9. c.SetCookie("site_cookie", "cookievalue", 3600, "/", "localhost", false, true)
  10. })
  11. router.Run()

SetCookie函数定义:
func (c Context) *SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
参数说明:

参数名 类型 说明
name string cookie名字
value string cookie值
maxAge int 有效时间,单位是秒,MaxAge=0 忽略MaxAge属性,MaxAge<0 相当于删除cookie, 通常可以设置-1代表删除,MaxAge>0 多少秒后cookie失效
path string cookie路径
domain string cookie作用域
secure bool Secure=true,那么这个cookie只能用https协议发送给服务器
httpOnly bool 设置HttpOnly=true的cookie不能被js获取到

2.获取cookie

  1. func Handler(c *gin.Context) {
  2. // 根据cookie名字读取cookie值
  3. data, err := c.Cookie("site_cookie")
  4. if err != nil {
  5. // 直接返回cookie值
  6. c.String(200,data)
  7. return
  8. }
  9. c.String(200,"not found!")
  10. }

3.删除cookie

通过将cookie的MaxAge设置为-1, 达到删除cookie的目的。

  1. func Handler(c *gin.Context) {
  2. // 设置cookie MaxAge设置为-1,表示删除cookie
  3. c.SetCookie("site_cookie", "cookievalue", -1, "/", "localhost", false, true)
  4. c.String(200,"删除cookie演示")
  5. }