Gin框架中gin-contrib/sessions的GetSession返回token为nil问题
我基于GIN框架+gin/sessions搭建了后端会话管理模块,前端采用Next.js。登录时通过SetSession函数已成功将token存入会话,但调用/check接口的GetSession获取会话时,token始终返回nil,提示“Session expired”。在SetSession内部检查会话状态正常,然而前端发起请求时却出现该问题,期望返回有效token以允许用户进入/dashboard页面,目前无法定位问题原因。
相关代码
GetSession函数
func GetSession(c *gin.Context) { session := sessions.Default(c) if value := session.Get("token"); value == nil { c.JSON(http.StatusUnauthorized, gin.H{ "message": "No token present/Session expired", "error": true, }) return } else { token := value.(string) tokenCheck, _ := lib.CheckSecureToken(token) if tokenCheck == false { c.JSON(http.StatusUnauthorized, gin.H{ "message": "Invalid token", "error": true, }) return } c.JSON(http.StatusOK, gin.H{ "message": "Token present", "token": value.(string), "error": false, }) } return }
SetSession函数
func SetSession(c *gin.Context, token string, status int) { session := sessions.Default(c) session.Set("token", token) err := session.Save() if err != nil { c.JSON(500, gin.H{ "message": "Error saving session, user not logged in", "error": true, "status": 500, }) } c.JSON(status, gin.H{ "message": "User logged in successfully", "token": token, "error": false, "status": 200, }) }
SetupRouter函数
func SetupRouter() *gin.Engine { r := gin.Default() config := cors.DefaultConfig() store := cookie.NewStore([]byte("")) //Secret is set store.Options(sessions.Options{ MaxAge: 60 * 60 * 24, }) r.Use(sessions.Sessions("usersession", store)) config.AllowOrigins = []string{"http://localhost:3000"} r.Use(cors.New(config)) r.GET("/", home) //Auth routes authGroup := r.Group("/api/v1/auth") //authGroup.POST("/logout", logoutUser) //authGroup.POST("/refresh", refreshUser) //authGroup.POST("/forgot", forgotPassword) //authGroup.POST("/reset", resetPassword) authGroup.POST("/login", loginUser) authGroup.POST("/signup", createUser) authGroup.GET("/check", handler.GetSession) }
loginUser函数
func loginUser(c *gin.Context) { var user database.User err := c.BindJSON(&user) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": "User not found"}) return } db, errS := database.LoginUser(&user) if errS != nil { c.JSON(http.StatusBadRequest, gin.H{"type": "Authentication Error", "message": "Invalid email or password", "status": "400"}) return } token := lib.GenerateSecureToken(user.Email) if db.IsAdmin { adminToken := lib.GenerateAdminSecureToken(user.Email) cookieAdmin := adminToken handler.SetAdminSession(c, cookieAdmin, 200) } handler.SetSession(c, token, 200) }
1. 补全CORS凭证支持
当前CORS配置未允许携带凭证(Cookie),前端请求时无法传递后端设置的Session Cookie。修改SetupRouter中的CORS配置:
config := cors.DefaultConfig() config.AllowOrigins = []string{"http://localhost:3000"} config.AllowCredentials = true // 开启凭证支持 config.AllowHeaders = append(config.AllowHeaders, "Authorization") // 可选,确保头部兼容 r.Use(cors.New(config))
同时前端请求需添加credentials: 'include',比如Next.js中使用fetch:
fetch('http://localhost:8080/api/v1/auth/check', { credentials: 'include' })
2. 设置有效Session密钥
代码中cookie.NewStore([]byte(""))的密钥为空(即使注释说已设置),会导致Session签名无效,浏览器无法正确解析Cookie。必须设置非空的随机密钥:
// 建议用openssl rand -hex 32生成随机字符串 store := cookie.NewStore([]byte("your-32-or-64-character-random-secret"))
3. 完善Cookie选项配置
补充Cookie的路径、安全属性,避免浏览器拦截:
store.Options(sessions.Options{ MaxAge: 60 * 60 * 24, Path: "/", // 确保Cookie在全站生效 HttpOnly: true, // 防XSS攻击 SameSite: http.SameSiteLaxMode, // 适配跨域场景 })
4. 修正SetSession的错误处理逻辑
当前SetSession中即使Session保存失败,仍会返回成功响应,导致前端误判登录状态。修改如下:
func SetSession(c *gin.Context, token string, status int) { session := sessions.Default(c) session.Set("token", token) err := session.Save() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "message": "Error saving session, user not logged in", "error": true, "status": http.StatusInternalServerError, }) return // 保存失败直接返回,不执行后续成功响应 } c.JSON(status, gin.H{ "message": "User logged in successfully", "token": token, "error": false, "status": http.StatusOK, }) }
5. 检查Session名称冲突
loginUser中如果用户是管理员,会同时调用SetAdminSession和SetSession,需确保两个Session使用不同的名称,避免互相覆盖token。比如SetAdminSession应使用独立的Session名称(如adminsession)。
6. 验证源地址一致性
确保前端请求的后端地址与AllowOrigins配置完全匹配,比如前端用http://localhost:3000,后端不能配置成http://127.0.0.1:3000,否则会被浏览器视为不同源,拒绝传递Cookie。
内容的提问来源于stack exchange,提问作者maciek_pl1234

