cookie和session

243 阅读4分钟

Cookie

cookie来源

http是无状态的,这样就代表每次请求都是独立的,不会受到前后请求的影响。所以会话中产生的数据不会被保留,而我们如果想要保持状态,就需要cookie

cookie是什么

在网络中,cookie实际上是指小量信息,是由web服务器创建,将信息存储在用户计算机(客户端)的数据文件。一般网络用户习惯用其复数形式cookies,主要用于某些网站为了辨别用户身份,进行session跟踪而存储在用户本地客户端上的数据,而这些数据通常会加密处理

cookie机制

cookie是有服务端生成,然后发送给客户端(一般是浏览器),浏览器会将cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就会发送该cookie给服务器(前提浏览器设置启用cookie),cookie的名称和值由服务器开发自己定义。这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等,服务器可以设置或读取cookies中包含的信息,借此维护用户跟服务器会话中的状态。

特点:

  • 浏览器发送请求时候,自动携带该站点之前存储的cookie信息。
  • 服务端可以设置cookie的数据
  • cookie是针对单个域名的,不同域名之间的cookie数据是独立的。
  • cookie数据可以设置过期时间,过期的cookie数据会被系统清除。

cookie查看

浏览器设置->隐私设置和自动填充->网站设置

image.png

go操作cookie

源码位置

标准库net/http中定义了cookie,他代表出现先http响应头set-cookie的值或http请求头中cookie的值。

type Cookie struct {
    Name       string
    Value      string
    Path       string
    Domain     string
    Expires    time.Time
    RawExpires string
    // MaxAge=0表示未设置Max-Age属性
    // MaxAge<0表示立刻删除该cookie,等价于"Max-Age: 0"
    // MaxAge>0表示存在Max-Age属性,单位是秒
    MaxAge   int
    Secure   bool
    HttpOnly bool
    Raw      string
    Unparsed []string // 未解析的“属性-值”对的原始文本
}

设置cookie

net/http中提供了如下SetCookie函数,它在w的头域中添加Set-Cookie头,该HTTP头的值为cookie。

func SetCookie(w ResponseWriter, cookie *Cookie) {
	if v := cookie.String(); v != "" {
		w.Header().Add("Set-Cookie", v)
	}
}

获取cookie

Request对象拥有两个获取cookie的方法和一个添加Cookie的方法:

获取Cookie的两种方法

// 解析并返回该请求的Cookie头设置的所有cookie
func (r *Request) Cookies() []*Cookie

// 返回请求中名为name的cookie,如果未找到该cookie会返回nil, ErrNoCookie。
func (r *Request) Cookie(name string) (*Cookie, error)

添加Cookie的方法:

// AddCookie向请求中添加一个cookie。
func (r *Request) AddCookie(c *Cookie)

gin框架操作cookie

第一次请求会走err,这时会设置cookie的key为gin_cookie,val为test image.png

在浏览器发送请求查看,此时在响应头部出现了set-cookie,但请求头部还没有

image.png

我们在调用一次,此时请求头部自动携带了cookie

image.png

session

由来

cookie虽然在一定程度上解决了保持状态的需求,但存在一些问题

  • 最大支持4096字节
  • 不安全,本身保存在客户端,可能被拦截 因此需要支持更多字节,保存在服务端,有较高安全性的东西,这就是session。

问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。

用户登陆成功之后,我们在服务端为每个用户创建一个特定的session和一个唯一的标识,它们一一对应。其中:

  • Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
  • 唯一标识通常称为Session ID会写入用户的Cookie中。

这样该用户后续再次访问时,请求会自动携带Cookie数据(其中包含了Session ID),服务器通过该Session ID就能找到与之对应的Session数据,也就知道来的人是“谁”。

总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在服务端为每个用户保存一个Session数据,该Session数据中能够保存具体的用户数据信息。

另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。

使用

使用第三方库: github.com/gin-contrib/sessions


package main

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

func main() {
  r := gin.Default()
  store := cookie.NewStore([]byte("secret"))
  r.Use(sessions.Sessions("mysession", store))

  r.GET("/hello", func(c *gin.Context) {
    session := sessions.Default(c)

    if session.Get("hello") != "world" {
      session.Set("hello", "world")
      session.Save()
    }

    c.JSON(200, gin.H{"hello": session.Get("hello")})
  })
  r.Run(":8000")
}