Gorilla CSRF - Forbidden - CSRF 令牌无效 - 当有两种形式时失败

问题描述

我正在添加 CSRF 令牌验证,但遇到了一个问题,我在页面上有两个表单,其中一个将成功提交,而另一个则不会。我不是在做 AJAX 请求,我只是在使用隐藏的输入字段。如果我在页面上只有表单时提交表单,则提交没有问题。如果我在一个包含多个表单的页面上提交它,它会失败。

下面是我的两个表单的模板代码

{{if .IsAuthenticated}}
  <form action='/admin/logout' method='POST'>
    <button>logout</button>
    {{.CsrfField}}
  </form>
{{end}}
<form action='/admin/stuff/create' method='POST'>
{{with .Form}}
  <div>
    <label>Title:</label>
    <input type='text' name='title' value='{{.Get "title"}}'>
  </div>
  <div>
    <input type='submit' value='Publish stuff'>
  </div>
{{end}}
{{.CsrfField}}
</form>

这就是生成的 HTML 的样子。两者似乎都有效。

enter image description here

不过,当我单击“注销”按钮时,出现 Forbidden - CSRF token invalid 错误,但单击第二个表单中的创建输入值始终有效。

当我尝试在主页“/admin/”上使用注销按钮时正确验证了它,但它在任何其他页面“/admin/snippet/:id”或“/admin”上不起作用/片段/创建”。 “注销”按钮是基本模板的一部分,因此它出现在每个页面上,因此它在任何页面上的显示方式应该没有任何不同。

我在页面上阅读了有关多个表单和 CSRF 令牌的其他 SO 帖子,我理解具有相同信息的多个表单应该没有问题,只要每个表单都有自己的表单,应该没问题.所以我不确定我哪里出错了。

解决方法

我发现了这个问题。目前 gorilla/csrf 的工作方式是,它不喜欢从一个路径创建掩码令牌,然后将该令牌发送到另一条路径。因此,在我的情况下,从 /admin/snippet/create/admin/logout 会引发错误,因为它期望令牌的路径为 /admin/snippet/<something>,因此它引发了错误。

此问题已在此 PR 中解决:https://github.com/gorilla/csrf/pull/147 基本上解决方案是自己将默认路径设置为所有路由都将包含的内容,因此在我的情况下为 /admin

这就是我现在在 main.go

中的 CSRF 声明的样子
var csrfMiddleWare = csrf.Protect(
        []byte("<put your 32 character key here>"),csrf.Path("/admin"),csrf.Secure(false),)

请注意,如果您遇到此问题,然后应用此修复程序但它没有解决问题,请尝试在单独的浏览器中进行测试,因为可能存在一些缓存问题。