如何在 Play 2.6 上禁用 CSRF 过滤器?

How can I disable the CSRF filter on Play 2.6?

我一直在尝试将 Postman 与我的 Play Framework API 一起使用,但我一直 运行 解决与 CSRF 过滤器相关的问题。

我浏览了几个论坛(包括 SO)以寻找解决此问题的方法,并且似乎添加了众所周知的建议:

play.filters.disabled+=play.filters.csrf.CSRFFilter

application.conf 文件。

我已经尝试执行该修复,但即使我这样做了,我仍然会在控制台日志中收到此错误:

[warn] p.filters.CSRF - [CSRF] Check failed because no token found in headers for /auth/logout

其中 /auth/logout 是 POST 请求,其中 Authorization cookie 设置为 JWT。

我尝试了一些替代修复,例如将 play.filters.disabled 设置为 []null,允许所有主机通过 CORS 过滤器,所有 3 的各种组合,等等,但最终我的日志中出现了同样的错误。

对应用程序所做的唯一更改是将 CSRF 令牌设置为 cookie 而不是在会话中,这会将错误更改为:

[warn] p.filters.CSRF - [CSRF] Check failed because none/none for /auth/logout

至少我知道 application.conf 正在正确加载,这让我感到欣慰,但它仍然没有解决问题。

是否有正确的方法来禁用我没有正确执行的 CSRF 过滤器?还有其他方法可以让 Postman 通过 CSRF 过滤器工作吗?

尝试在您的 application.conf 中添加此代码:

play.filters.hosts {
  allowed = ["."]
}

让我知道它是否适合您。 也检查官方播放 documentation.

好的,我找到了问题所在,但要挖掘问题的根源有点棘手。

当我将 macwire 添加到依赖项注入项目时,我必须定义一个自定义应用程序加载器,它需要一个自定义 AppComponents class。我对 AppComponents class 的声明看起来像这样:

class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) with MyModule with AssetsComponents with I18nComponents with play.filters.HttpFiltersComponents

现在很明显,play.filters.HttpFiltersComponents 具有以下字段:

def httpFilters: Seq[EssentialFilter] = Seq(csrfFilter, securityHeadersFilter, allowedHostsFilter)

其中 csrfFiltersecurityHeadersFilterallowedHostsFilter 是有效的硬编码值。我想当使用自定义 ApplicationLoader 时,它会忽略 application.conf 文件中设置的 http 过滤器(在我写这篇文章时这似乎很明显)。

我正在使用的当前 workaround/fix 是设置 val httpFilters: Seq[EssentialFilter] = Seq(securityHeadersFilter, allowedHostsFilter),而不是默认的 HttpFiltersComponents 字段。我可能会稍微修改一下以从配置文件中读取过滤器,但现在这可以解决问题。

TL;DR: 在集成 MacWire 时添加了自定义 ApplicationLoader,没有意识到这会影响配置文件中的字段。

编辑

我的 ApplicationLoader 中用于使用 macwire 配置 HTTP 过滤器的最终代码如下所示,以防其他人需要它。它使用与 HttpFiltersComponents 几乎完全相同的代码,但使用 macwire 而不是 Guice(未定义的变量继承自 BuiltInComponentsFromContext):

override lazy val httpFilters: Seq[EssentialFilter] = {
    val wiredInstance: Wired = wiredInModule(this)
    val classes: Seq[Class[EssentialFilter]] = {
        val disabledFilters = config.get[Seq[String]]("play.filters.disabled")
        val enabledFilters = config.get[Seq[String]]("play.filters.enabled").filterNot(disabledFilters.contains)
        try {
            for (filterClassName <- enabledFilters) yield {
                try {
                    environment.classLoader.loadClass(filterClassName).asInstanceOf[Class[EssentialFilter]]
                } catch {
                    case e: ClassNotFoundException =>
                      throw configuration.reportError("play.filters.enabled", s"Cannot load class $filterClassName", Some(e))
                }
            }
        } catch {
            case e: ConfigException.Null =>
                Nil
            case e: ConfigException.Missing =>
                Nil
        }
    }
    classes.map(x => wiredInstance.wireClassInstance(x))
}