gin-csrf 与 gin-contrib/sessions 不兼容导致 403 或 token invalid,根本原因是 session store 实例未共享;需显式复用同一 sessions.store 实例,并确保 csrf 中间件在 session 后注册,且 token 需手动注入模板或响应头。

gin-csrf 是目前 Gin 生态中最轻量、最易集成的 CSRF 防护中间件,但它不兼容 gin-contrib/sessions 的默认配置——这是你启用后 403 或 token 不生效的首要原因。
为什么 gin-csrf 中间件返回 403 或 token 总是 invalid?
根本问题在于 session 存储未对齐:gin-csrf 默认依赖 gorilla/sessions 的 cookie store,而多数项目用的是 gin-contrib/sessions(底层也是 gorilla,但封装层屏蔽了 store 实例暴露)。中间件拿不到原始 session.Store,就无法读写 token 所需的 session key。
- 现象:表单提交时返回
403 Forbidden,或前端拿到的_csrf字段为空/过期 - 本质:CSRF token 存在 session 中,但中间件和你的 session 初始化用了两套不互通的 store 实例
- 解法不是换库,而是显式复用同一个
session.Store实例
如何让 gin-csrf 和 gin-contrib/sessions 共享 session store?
关键点:把 gin-contrib/sessions 创建的 store 提前导出,传给 gin-csrf 初始化函数。不能靠全局变量或重定义,必须传递实例引用。
- 先创建
session.Store,例如:store := sessions.NewCookieStore([]byte("session-secret")) - 用它初始化
gin-contrib/sessions中间件:gin.Default().Use(sessions.Sessions("mysession", store)) - 再传给
gin-csrf:csrf.Middleware(store, csrf.Secret("csrf-secret")) - 注意:两个中间件必须注册在同一个
*gin.Engine上,且csrf.Middleware要在 session 中间件之后
gin-csrf 的 token 如何在 HTML 表单中正确使用?
它不像 gorilla/csrf 那样自动注入模板变量,你需要手动从 context 获取并渲染——这也是容易漏掉的一环。
- 在 handler 中调用:
token := csrf.GetToken(c),然后通过c.HTML传入模板 - 模板里必须显式写入 hidden input:
<input type="hidden" name="_csrf" value="{{.CSRFToken}}"> - 别依赖 JS 自动取 cookie:CSRF token 是服务端绑定 session 的,cookie 里没有明文 token,只有加密后的 session ID
- 如果用 AJAX 提交,需从 DOM 读取 hidden input 的 value,或在响应头加
X-CSRF-Token并由 JS 拦截设置
哪些请求路径不该加 CSRF 中间件?
CSRF 保护只对状态变更操作有意义,盲目全局启用会破坏 API 兼容性和前端调试流程。
- 明确排除:
/api/下的 REST 接口(尤其带Authorization: Bearer的),这类请求本就不依赖 Cookie 认证 - 静态资源路径如
/static/、/healthz等,无需防护 - 登录、注册接口本身需要 CSRF 保护(防止攻击者伪造注册/登录请求),但要确保 token 在 GET 登录页就已下发
- 最稳妥做法:用
router.Group("/submit").Use(csrf.Middleware(...))显式包裹敏感路由组











