Fiber的session.New()必须配合store使用,否则Set/Get无效;store需全局复用且与csurf.SessionStore一致,Save()必须显式调用才能持久化。

session.New() 必须配合 store 使用,不能单独调用
Fiber 的 session.New() 本身不存数据,它只是个会话管理器工厂;真正存储 session 数据的是你传进去的 store 实例。漏掉 store 或传 nil,运行时不会报错,但所有 c.Session().Get() 都返回零值,Set() 也不生效——这是最常被忽略的初始化陷阱。
常见搭配有三种:
-
memory.New():开发调试可用,重启即丢,不支持多实例 -
redis.New():生产必备,需传入*redis.Client,K8s 多副本唯一可靠选择 -
cookie.New():纯前端存储,敏感数据(如用户 ID、权限)绝不可放这里,仅适合非关键状态(如主题偏好)
示例正确写法:
store := redis.New(redis.Config{Client: rdb}) // rdb 是 *redis.Client<br>sess := session.New(session.Config{<br> Storage: store,<br> CookieHTTPOnly: true,<br> CookieSecure: true, // HTTPS 环境下必须开<br>})
中间件注册位置决定 session 是否可被后续 handler 读取
session 中间件必须在路由定义前注册,且不能挂载到子路径前缀下(比如 app.Use("/api", sess)),否则 c.Session() 在 /login 这类非 /api 路由里拿不到实例。
正确方式只有两种:
- 全局注册:
app.Use(sess)—— 所有请求都带 session 上下文 - 按组注册:
auth := app.Group("/auth"); auth.Use(sess)—— 仅该组内路由可用,/auth/login可用,/public/logo.png不用
错误写法:app.Get("/login", sess, handler) 看似“精准”,但 sess 在此是普通 handler,不是中间件链入口,c.Session() 会 panic:nil pointer dereference。
Session 值读写必须显式 Save(),且不能跨请求复用 ctx.Session()
Fiber 的 session 实例不是线程安全的,也不跨请求缓存。每次请求进来,c.Session() 返回一个新实例;你调 .Set("user_id", 123) 后若不 .Save(c),这个值永远不会落地。
典型翻车现场:
- 忘记
Save():写完sess.Set("logged_in", true)就 return,前端下次请求仍为未登录 - 在中间件里保存后,又在 handler 里重复
c.Session().Get()却没再.Save():修改丢失 - 把
sess存成包级变量,试图复用:并发下数据错乱,甚至 panic
正确流程固定三步:
sess, _ := c.Session()<br>sess.Set("user_id", userID)<br>sess.Save(c) // 必须这一步
CSRF 和 session 必须联动,否则 token 校验永远失败
Fiber 的 csurf.New() 默认依赖 session 存储 token。如果你用了 redis.New() 作 session store,但 csurf 没配 SessionStore 字段,它会 fallback 到内存 store,导致 CSRF token 生成和验证不在同一个存储空间——前端拿到 token,后端校验时查不到,一律 403。
必须显式桥接:
- 先创建 session store(如
redis.New(...)) - 再传给 csurf:
csurf.New(csurf.Config{<br> SessionStore: store, // 和 session.New() 用同一个 store 实例<br> CookieHTTPOnly: true,<br>}) - 两者注册顺序无关,但 store 实例必须相同
另一个坑:如果 session cookie 设了 SameSite: Lax(默认),而前端跨域发 POST,CSRF token 可能因 cookie 不随请求发送而校验失败——此时要么前端改用 SameSite: None + Secure,要么后端换用双提交 Cookie 模式(不依赖 session)。











