我在Koltin编写了一个Vertx-web处理程序,它将我收到的任何HTTP请求重定向到HTTPS,并且我使用context.request().isSSL
来确定请求是否不是SSL,这很好用直到我把我的代码放在负载均衡器后面。如果负载均衡器在HTTPS上与我的Vertx-web服务器通信,则它认为所有用户请求都是HTTPS,即使它们不是。如果我更改负载均衡器以在HTTP上与Vertx-web通信,那么即使用户已使用HTTPS,每个请求也会无限重定向。
然后我还看到另一个问题,即使用context.request().absoluteURI()
的重定向转到私有地址而不是用户实际与之通信的公共可用地址。
Vertx-web中是否有一个处理程序,我不知道这样做,还是一些惯用的解决方法?我应该从JavaScript执行此操作,因为它看到真实的用户地址而不是尝试服务器端重定向?
我在Kotlin编码,因此该语言的任何示例都很棒!
注意: 此问题是由作者(Self-Answered Questions)故意编写和回答的,因此可以在SO中分享有趣问题的解决方案。
答案 0 :(得分:2)
首先,最好是您的代理或负载均衡器可以为您执行此检查并重定向,因为它了解公共URL,并且在首次与用户联系时是一个更简单的过程。但是,您也可以通过更复杂的方式在服务器端执行此操作。
您要检查的标记context.request().isSSL
仅对Vertx-web的传入连接有效,并且不认为最终用户连接到您的代理或负载均衡器。您需要使用X-Forwarded-Proto
标头(有时是X-Forwarded-Scheme
)并检查用户的实际协议。并且只有当该标头不存在时,您才能使用context.request().isSSL
您还需要外部化自己的网址,以便能够在服务器端重定向到浏览器可以用来查找您的公共网址。
首先,RoutingContext.externalizeUrl()
的Stack Overflow回答中有一个Kotlin函数,你需要它:
I have a Vertx request and I need to calculate an externally visible (public) URL
然后知道您的公共URL,您可以使用以下处理程序,该处理程序具有预期公共HTTPS端口的默认值(默认443将从URL 中消失),这是重定向的形式( ie 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()
}
}
}