最近使用 go+gin 开发网站应用,在处理用户登录状态开发的过程中我选择使用 gin 官方提供的 session 解决方案,也就是 gin-contrib/sessions
项目地址: https://github.com/gin-contrib/sessions
中间件的使用也很简单,官方 demo 如下
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")
}
这个中间件用起来很好,但是也在使用过程中发现了一些问题。
最常见的问题就是登陆前和登陆后个别页面会出现 session 获取不到的情况。
问题演示: Gin 路由如下
r.GET("/i/*name", func(c *gin.Context) {views.testViews(c)
})
上面的路由 i /a i/b 其实都是走的一样的处理逻辑,我们使用中间件可以在登陆后获取和判断登录状态,但是往往就会出现没登陆时 i / a 是可以访问的,当我们登录之后 i /a 和 i / b 获取的 session 状态应该是一样的。但是真实的情况很可能出现 i / b 这个新访问页面是能够获取 session 状态的,而 i / a 却始终获取不到
问题的原因往往出现于 session 的设置上面,未登录时使用 session 设置的值 path 路径是当前页面而不是 / 所以也就会出现登陆后 session 取不到问题。
解决这个问题的方法也很简单,我们只需要在设置 session 的时候 Path 都设置为 / 就好了。
store, _ := redis.NewStoreWithDB(10, "tcp")
store.Options(sessions.Options{
Path: "/",
MaxAge: 86400 * 90,
})
r.Use(sessions.Sessions("session", store))
在具体的逻辑处理里面不要单纯的只设置 MaxAge,要把 Path 也带上
session.Set("uuid", u4.String())
session.Options(sessions.Options{MaxAge: 86400 * 90, Path: "/"})
session.Save()
这样就不会出现一些页面能获取到一些页面获取不到 session 的问题了。
参考资料如下:
https://github.com/gin-contrib/sessions/issues/153