Gin -- 会话控制

10.1 cookie

1. 概述

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

2. 操作方法

ctx.Cookie("cookie的key")
SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)

// name为cookie的key
// value为cookie的值,
// maxAge为过期时间,单位为秒,
// path为给哪个url设置cookie
// domain为本服务器的ip
// secure为是否只能通过https访问
// httpOnly为是否允许别人通过js获取自己的cookie

c.SetCookie("name","xiaohuang",60,"/login","127.0.0.1",false,true)

3. 示例

func indexHandler(c *gin.Context) {
	// 客户端是否携带Cookie
	cookie, err := c.Cookie("key_cookie")
	if err != nil{
		cookie = "NoSet"
        // 设置cookie函数
		c.SetCookie("name","xiaohuang",60,"/login","127.0.0.1",false,true)
	}
	fmt.Println(cookie)
	c.HTML(http.StatusOK,"user/index.html",gin.H{"title": "我是测试", "address": "www.5lmh.com"})
}

3. 练习

1. 模拟实现权限验证中间件

  1. 有两个路由:login和home
  2. login用于设置cookie
  3. home是访问查看信息的请求
  4. 在请求home之前,先跑中间件代码,检验是否存在cookie
  5. 访问home,会显示错误,因为权限校验未通过

由于我使用的是不同app的不同路由,我的目录结构如下:

编写中间件

func MiddleWare() gin.HandlerFunc {
	return func(c *gin.Context) {
                // 通过请求路径,避开登录时验证cookie
		if c.Request.RequestURI != "/userlogin"{
			fmt.Println(">>>>中间件开始执行")
			cookieStr,err := c.Cookie("authStr")
			if err != nil{
				fmt.Println("cookie 为空")
				// 设置变量到Context的key中,可以通过Get()取,在Handler中通过判断middleware的值,返回对应的错误
				c.Set("middleware","nil")
			}

			if cookieStr != "awersxcxcfwefbweva"{
				fmt.Println("cookie错误")
				// 设置变量到Context的key中,可以通过Get()取
				c.Set("middleware","err")

			}
			status := c.Writer.Status()
			fmt.Println("中间件执行完毕>>>",status)
		}

	}
}

注册Middleware

func Init() *gin.Engine {
	r := gin.New()
	
	// 注册全局中间件
	r.Use(MiddleWare())
	for _, opt := range options{
		opt(r)
	}
	return r
}

Handler根据不同的middleware值返回不同的错误

func userLoginHandler(c *gin.Context) {
	var userLogin model.Login

        // 获取中间件设置的值,进行判断

	errStr,exit := c.Get("middleware")
	if !exit{
		fmt.Println(">>>>> 后端设置错误")
	}
	if errStr == "nil"{
		c.JSON(http.StatusOK, gin.H{"code": 40000003, "error": "cookie不允许为nil", "data": ""})
		return
	}
	if errStr == "err"{
		c.JSON(http.StatusOK, gin.H{"code": 40000004, "error": "cookie串不合法", "data": ""})
		return
	}
	if err := c.ShouldBindJSON(&userLogin); err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 40000001, "error": "参数错误", "data": ""})
		return
	}

	if userLogin.User != "root" || userLogin.Password != "admin" {
		c.JSON(http.StatusOK, gin.H{"code": 40000002, "error": "账号或密码错误", "data": ""})
		return
	}

	c.SetCookie("authStr", "awersxcxcfwefbweva", 60, "/userlogin", "127.0.0.1", false, true)
	c.JSON(http.StatusOK, gin.H{"code": 20000000, "error": "登录成功", "data": "hello"})
}

第二种方式

func AuthMiddleWare() gin.HandlerFunc {
   return func(c *gin.Context) {
      // 获取客户端cookie并校验
      if cookie, err := c.Cookie("abc"); err == nil {
         if cookie == "123" {
            c.Next()
            return
         }
      }
      // 返回错误
      c.JSON(http.StatusUnauthorized, gin.H{"error": "err"})
      // 若验证不通过,不再调用后续的函数处理
      c.Abort()
      return
   }
}

4. 缺点

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

2. session

0. 官网地址

http://www.gorillatoolkit.org/pkg/sessions

1. 概述

gorilla/sessions为自定义session后端提供cookie和文件系统session以及基础结构。
主要功能是:

  1. 简单的API:将其用作设置签名(以及可选的加密)cookie的简便方法。
  2. 内置的后端可将session存储在cookie或文件系统中。
  3. Flash消息:一直持续读取的session值。
  4. 切换session持久性(又称“记住我”)和设置其他属性的便捷方法。
  5. 旋转身份验证和加密密钥的机制。
  6. 每个请求有多个session,即使使用不同的后端也是如此。
  7. 自定义session后端的接口和基础结构:可以使用通用API检索并批量保存来自不同商店的session。

2. 示例

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/sessions"
)

// 初始化一个cookie存储对象
// something-very-secret应该是一个你自己的密匙,只要不被别人知道就行
var store = sessions.NewCookieStore([]byte("something-very-secret"))

func main() {
    http.HandleFunc("/save", SaveSession)
    http.HandleFunc("/get", GetSession)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        fmt.Println("HTTP server failed,err:", err)
        return
    }
}

func SaveSession(w http.ResponseWriter, r *http.Request) {
    // Get a session. We're ignoring the error resulted from decoding an
    // existing session: Get() always returns a session, even if empty.

    // 获取一个session对象,session-name是session的名字
    session, err := store.Get(r, "session-name")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 在session中存储值
    session.Values["foo"] = "bar"
    session.Values[42] = 43
    // 保存更改
    session.Save(r, w)
}
func GetSession(w http.ResponseWriter, r *http.Request) {
    session, err := store.Get(r, "session-name")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    foo := session.Values["foo"]
    fmt.Println(foo)
}

3. 操作方法

1. 删除session

// 将session的最大存储时间设置为小于零的数即为删除
session.Options.MaxAge = -1
session.Save(r, w)
posted @ 2024-01-17 17:05  河图s  阅读(18)  评论(0)    收藏  举报