部署在代理后面时,Grails 会生成正确的链接

Pie*_*ent 5 apache https grails tomcat

请考虑对已部署的 Grails 应用程序进行以下设置。

  • Grails 应用程序部署在 Tomcat 服务器 (tomcat7) 上
  • 在 Tomcat 前面部署了 Apache Web 服务器
  • Apache 进行 SSL 卸载,并充当 Tomcat 的代理

到目前为止,这是一个相当标准的设置,我已经成功使用过很多次。我现在的问题是 Grails 应用程序生成的链接,尤其是那些重定向的链接(标准控制器重定向,这种情况一直发生,例如在成功发布表单后)。

到目前为止,有一项配置与所有其他应用程序不同:在此 Grails 应用程序中没有配置serverURL 。该应用程序是一个多租户应用程序,其中每个租户都有自己的子域。(因此,如果应用程序通常在https://www.example.com下运行,则租户可以使用https://tenant.example.com。)子域是自动设置的,即无需在 DNS 或 Apache 级别进行任何配置。Grails 可以通过在 Config.groovy 中省略 serverURL 属性来完美地做到这一点:然后它通过检查客户端主机来解析所有 url。

然而:当生成重定向 URL 时,Grails 并不知道该网站正在 https 下运行。所有重定向 url 都以 http 开头...我想这并不奇怪,因为在应用程序中没有配置我们使用 https:没有 serverURL 配置,并且从技术上讲,应用程序在 Tomcat 的标准 http 端口上运行,因为Apache 的 SSL 卸载和代理。

那么,底线是:我该怎么做才能使 Grails 生成正确的重定向?

我尝试扩展 DefaultLinkGenerator 并覆盖 makeServerURL() 方法。像这样:

class MyLinkGenerator extends DefaultLinkGenerator {

MyLinkGenerator(String serverBaseURL, String contextPath) {
    super(serverBaseURL, contextPath)
}

MyLinkGenerator(String serverBaseURL) {
    super(serverBaseURL)
}

def grailsApplication

/**
 * @return serverURL adapted to deployed scheme
 */
String makeServerURL() {
    // get configured protocol
    def scheme = grailsApplication.config.grails.twt.baseProtocol ?: 'https://'
    println "Application running under protocol $scheme"

    // get url from super
    String surl = super.makeServerURL()
    println "> super.makeServerURL(): $surl"
    if (surl) {
        // if super url not matching scheme, change scheme
        if (scheme=='https://' && surl?.startsWith('http://')) {
            surl = scheme + surl?.substring(7)
            println "> re-written: $surl"
        }
    }

    return surl

}

}
Run Code Online (Sandbox Code Playgroud)

(也许不是最漂亮的代码,但我希望它解释了我想做的事情。并且我遗漏了有关在 resources.groovy 中配置此类的内容。)

运行这段代码时会发生奇怪的事情:

  • 在日志中,您可以看到正在执行的代码,以及正在生成的更改后的 url (http > https),​​但是...
  • 发送到浏览器的重定向是未更改的 url (http)
  • 更糟糕的是:生成的视图中的所有资源都被破坏了:它们现在都以 // 开头(所以应该是相对的“/css/myapp.css”现在是“//css/myapp.css”)

任何帮助或见解将不胜感激!

顺便说一句,Grails 版本是 2.1.1(升级有点落后......)。

小智 3

看起来你总是在与外界谈论 https,所以你最干净的选择是解决问题的根源,在你的 Apache Web 服务器上。添加到 httpd.conf Header edit Location ^http://(.*)$ https://$1,就完成了。

如果您的限制迫使您在应用程序中解决这个问题,您可以在拦截器之后重写LocationGrails 中的标头字段。该解决方案的详细信息在这篇文章中。