在 Vertx 中,我需要将所有 HTTP 请求重定向到相同的 URL,但对于 HTTPS

2024-03-23

我在 Koltin 中编写了一个 Vertx-web 处理程序,它将我收到的任何 HTTP 请求重定向到 HTTPS,并且我正在使用context.request().isSSL确定请求是否不是 SSL,在我将代码放在负载均衡器后面之前,这一切都很好。如果负载均衡器通过 HTTPS 与我的 Vertx-web 服务器通信,那么它会认为所有用户请求都是 HTTPS,即使它们不是。如果我更改负载均衡器以通过 HTTP 与 Vertx-web 通信,那么即使用户已经在使用 HTTPS,每个请求也会无限重定向。

然后我还看到另一个问题,即重定向使用context.request().absoluteURI()转到私人地址而不是用户实际正在交谈的公开可用地址。

Vertx-web 中是否有我缺少的处理程序可以执行此操作,或者有一些惯用的方法来解决此问题?我应该从 JavaScript 中执行此操作,因为它会看到真实的用户地址,而不是尝试服务器端重定向吗?

我正在使用 Kotlin 进行编码,因此该语言的任何示例都很棒!

Note: 这个问题是作者特意写出来并回答的(自答问题 https://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/),以便在 SO 中共享有趣问题的解决方案。


首先,最好您的代理或负载均衡器可以为您执行此检查和重定向,因为它了解公共 URL,并且在第一次与用户联系时是一个更简单的过程。但是,您也可以在服务器端执行此操作,但稍微复杂一些。

您正在检查的标志,context.request().isSSL仅对 Vertx-web 的传入连接有效,不考虑最终用户与代理或负载均衡器的连接。您需要使用X-Forwarded-Proto标题(有时X-Forwarded-Scheme)并检查用户的实际协议。并且只有当该标头不存在时您才可以使用context.request().isSSL

您还需要外部化您自己的 URL,以便能够在服务器端重定向到浏览器可以用来找到您的公共 URL。

首先,这个 Stack Overflow 答案中有一个 Kotlin 函数RoutingContext.externalizeUrl(),您将在这里需要它:
我有一个 Vertx 请求,我需要计算一个外部可见(公共)URL https://stackoverflow.com/questions/39564199/i-have-a-vertx-request-and-i-need-to-calculate-an-externally-visible-public-ur/39564200#39564200

然后知道您的公共 URL,您可以使用以下处理程序,该处理程序具有预期公共 HTTPS 端口的默认值(默认 443 将从 URL 中消失),哪种形式的重定向(i.e. 302),以及如果路由失败或继续的任何异常:

fun Route.redirectToHttpsHandler(publicHttpsPort: Int = 443, redirectCode: Int = 302, failOnUrlBuilding: Boolean = true) {
    handler { context ->
        val proto = context.request().getHeader("X-Forwarded-Proto")
                ?: context.request().getHeader("X-Forwarded-Scheme")
        if (proto == "https") {
            context.next()
        } else if (proto.isNullOrBlank() && context.request().isSSL) {
            context.next()
        } else {
            try {
                val myPublicUri = URI(context.externalizeUrl())
                val myHttpsPublicUri = URI("https", 
                        myPublicUri.userInfo, 
                        myPublicUri.host, 
                        publicHttpsPort,
                        myPublicUri.rawPath, 
                        myPublicUri.rawQuery, 
                        myPublicUri.rawFragment)
                context.response().putHeader("location", myHttpsPublicUri.toString()).setStatusCode(redirectCode).end()
            } catch (ex: Throwable) {
                if (failOnUrlBuilding) context.fail(ex)
                else context.next()
            }
        }
    }
}

一个更简单的版本可能是只信任context.externalizeUrl类并查看它是否具有正确的协议和端口,如果不正确则重定向:

fun Route.simplifiedRedirectToHttpsHandler(publicHttpsPort: Int = 443, redirectCode: Int = 302, failOnUrlBuilding: Boolean = true) {
    handler { context ->
        try {
            val myPublicUri = URI(context.externalizeUrl())
            if (myPublicUri.scheme == "http") {
                val myHttpsPublicUri = URI("https",
                        myPublicUri.userInfo,
                        myPublicUri.host,
                        publicHttpsPort,
                        myPublicUri.rawPath,
                        myPublicUri.rawQuery,
                        myPublicUri.rawFragment)
                context.response().putHeader("location", myHttpsPublicUri.toString()).setStatusCode(redirectCode).end()
            }
            else {
                context.next()
            }
        } catch (ex: Throwable) {
            if (failOnUrlBuilding) context.fail(ex)
            else context.next()
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Vertx 中,我需要将所有 HTTP 请求重定向到相同的 URL,但对于 HTTPS 的相关文章

随机推荐