12-Gin的Cookie,Session.md
一 Cookie介绍和使用
1.1 介绍
HTTP 是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页 面,服务器无法认识到这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何 关系的。如果我们要实现多个页面之间共享数据的话我们就可以使用 Cookie,Session或Token实 现
cookie 是存储浏览器中的键值对,可以让我们用同一个浏览器访问同一个域名 的时候共享数据
1.2 Gin中Cookie的设置
c.SetCookie(name, value string, maxAge int, path, domain string, secure,httpOnly bool)
// name:cookie的key值
// value:cookie的value值
// maxAge:过期时间,如果只想设置Cookie的保存路径而不想设置存活时间,可以设置为 nil
// path:cookie 的路径
// domain:cookie 的路径 Domain 作用域 本地调试配置成 localhost , 正式上线配置成域名
// secure:当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中 才有效
// httpOnly:是微软对 COOKIE 做的扩展。如果在 COOKIE 中设置了"httpOnly"属性,则通过程序(JS 脚本、applet 等)将无法读取到 COOKIE 信息,防止 XSS 攻击产生
1.3 Gin中Cookie的获取
cookie, err := c.Cookie("key值")
1.4 设置获取案例
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("set_cookie", func(c *gin.Context) {
c.SetCookie("name", "lqz", 60, "/", "", false, true)
c.SetCookie("age", "19", 600, "", "", false, true)
c.String(200, "cookie设置成功")
})
r.GET("get_cookie", func(c *gin.Context) {
name, err := c.Cookie("name")
if err != nil {
c.String(200, "cookie获取打印失败,错误是:%s",err)
return
}
fmt.Println("name的cookie值为:",name)
c.String(200, "cookie获取打印成功")
})
r.Run(":8080")
}
1.5 Cookie登陆认证练习
- 模拟实现权限验证中间件
- 有2个路由,login和home
- login用于设置cookie
- home是访问查看信息的请求
- 在请求home之前,先跑中间件代码,检验是否存在cookie
- 访问home,会显示错误,因为权限校验未通过
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func AuthMiddleWare() gin.HandlerFunc {
return func(c *gin.Context) {
// 获取客户端cookie并校验
if cookie, err := c.Cookie("login"); err == nil {
if cookie == "yes" {
c.Next()
}
} else {
// 返回错误
c.JSON(http.StatusUnauthorized, gin.H{"error": "没有登录"})
// 若验证不通过,不再调用后续的函数处理
c.Abort()
}
}
}
func main() {
r := gin.Default()
r.GET("/login", func(c *gin.Context) {
c.SetCookie("login", "yes", 60, "/", "", false, true)
// 返回信息
c.String(200, "Login success!")
})
r.GET("/home", AuthMiddleWare(), func(c *gin.Context) {
c.JSON(200, gin.H{"data": "登陆成功,能访问home"})
})
r.Run(":8080")
}
二 Session的介绍和使用
2.1 Session介绍
session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 session 保存在服务器上。
2.2 Session工作流程
当客户端浏览器第一次访问服务器并发送请求时,服务器端会创建一个 session 对象,生成 一个类似于 key,value 的键值对,然后将 value 保存到服务器 将 key(cookie)返回到浏览器(客 户)端。浏览器下次访问时会携带 key(cookie),找到对应的 session(value)。
2.3 Gin中使用 Session
Gin 官方没有给我们提供 Session 相关的文档,这个时候我们可以使用第三方的 Session 中间 件来实现
推荐一:gin-contrib/sessions后起之秀
https://github.com/gin-contrib/sessions
推荐二:gorilla/sessions年久失修
2.5 gin-contrib/sessions使用
下载:go get github.com/gin-contrib/sessions
2.5.1 session放在内存中
package main
import (
"fmt"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 创建基于 cookie 的存储引擎,lqzisnb 参数是用于加密的密钥
store := cookie.NewStore([]byte("lqzisnb"))
// 设置 session 中间件,参数 sessionid,指的是 session 的名字,也是 cookie 的名字
//store是前面创建的存储引擎,我们可以替换成其他存储引擎
r.Use(sessions.Sessions("sessionid", store))
r.GET("/set_session", func(c *gin.Context) {
//初始化 session 对象
session := sessions.Default(c) //设置过期时间
// 过期时间6h
session.Options(sessions.Options{MaxAge: 3600 * 6})
//设置 Session
session.Set("username", "lqz")
session.Save()
c.JSON(200, gin.H{"msg": "设置session成功----userrname:lqz"})
})
r.GET("/get_session", func(c *gin.Context) {
session := sessions.Default(c)
// 通过 session.Get 读取 session 值
username := session.Get("username")
fmt.Println(username)
c.JSON(200, gin.H{"msg": "获取session成功"})
})
r.Run(":8080")
}
2.5.2 session放在redis中
如果我们想将 session 数据保存到 redis 中,只要将 session 的存储引擎改成 redis 即可。 使用 redis 作为存储引擎的例子,首先安装 redis 存储引擎的包
go get github.com/gin-contrib/sessions/redis
package main
import (
"fmt"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/redis"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 初始化基于 redis 的存储引擎
// size:redis 最大的空闲连接数
//network: 数通信协议tcp或者udp
//address:redis 地址, 格式,host:port
//password:redis密码
//最后一个参数:session 加密密钥
store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))
r.Use(sessions.Sessions("sessionid", store))
r.GET("/set_session", func(c *gin.Context) {
session := sessions.Default(c)
session.Options(sessions.Options{MaxAge: 3600 * 6})
//设置 Session
session.Set("username", "pyy")
session.Save()
c.JSON(200, gin.H{"msg": "设置session到reids成功----userrname:pyy"})
})
r.GET("/get_session", func(c *gin.Context) {
session := sessions.Default(c)
// 通过 session.Get 读取 session 值
username := session.Get("username")
fmt.Println(username)
c.JSON(200, gin.H{"msg": "获取session成功"})
})
r.Run(":8080")
}
2.6 gorilla/sessions使用
go get github.com/gorilla/sessions
2.6.1 gin框架使用
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"github.com/gorilla/sessions"
)
// 初始化一个cookie存储对象
// something-very-secret应该是一个你自己的密匙,只要不被别人知道就行
var store = sessions.NewCookieStore([]byte("asdfasdf"))
func main() {
r:=gin.New()
r.GET("/savesession", func(context *gin.Context) {
// Get a session. We're ignoring the error resulted from decoding an
// existing session: Get() always returns a session, even if empty.
session, err := store.Get(context.Request, "session-name")
if err != nil {
context.String(http.StatusInternalServerError,"出错了")
return
}
// 在session中存储值
session.Values["name"] = "lqz"
session.Values["age"] = 18
session.Values[42] = 43
// 保存更改
session.Save(context.Request, context.Writer)
context.String(200,"保存session成功")
})
r.GET("getsession", func(context *gin.Context) {
session, err := store.Get(context.Request, "session-name")
if err != nil {
context.String(http.StatusInternalServerError,"出错了")
return
}
name := session.Values["name"]
age := session.Values["age"]
count:=session.Values[42]
fmt.Println(name)
fmt.Println(age)
fmt.Println(count)
context.String(200,"查询成功")
})
r.Run()
}
2.6.2 net/http使用
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)
}
2.6.3 删除session的值
// 删除
// 将session的最大存储时间设置为小于零的数即为删除
session.Options.MaxAge = -1
session.Save(r, w)