在 Vertx 中,我需要将所有 HTTP 请求重定向到相同的 URL 但对于 HTTPS
In Vertx I need to redirect all HTTP requests to the same URL but for HTTPS
我在 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 .
首先,最好是您的代理或负载均衡器可以为您执行此检查和重定向,因为它知道 public URL 并且在第一次接触时是一个更简单的过程用户。但是,您也可以 server-side 稍微复杂一点。
您正在检查的标志 context.request().isSSL
仅对 Vertx-web 的传入连接有效,不考虑 end-user 与您的代理或负载平衡器的连接。您需要使用 X-Forwarded-Proto
header(有时 X-Forwarded-Scheme
)并检查用户的实际协议。只有当 header 不存在时,您才可以使用 context.request().isSSL
您还需要外部化您自己的 URL 以便能够在服务器端重定向到浏览器可以用来找到您的东西,您的 public URL。
首先,RoutingContext.externalizeUrl()
的 Stack Overflow 答案中有一个 Kotlin 函数,您将在此处需要它:
I have a Vertx request and I need to calculate an externally visible (public) URL
然后知道您的 public URL 您可以使用以下处理程序,它具有预期 public HTTPS 端口的默认值(默认 443 将从 URL),哪种形式的重定向(即 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
class 并查看它是否具有正确的协议和端口,如果不正确则重定向:
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()
}
}
}
我在 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 .
首先,最好是您的代理或负载均衡器可以为您执行此检查和重定向,因为它知道 public URL 并且在第一次接触时是一个更简单的过程用户。但是,您也可以 server-side 稍微复杂一点。
您正在检查的标志 context.request().isSSL
仅对 Vertx-web 的传入连接有效,不考虑 end-user 与您的代理或负载平衡器的连接。您需要使用 X-Forwarded-Proto
header(有时 X-Forwarded-Scheme
)并检查用户的实际协议。只有当 header 不存在时,您才可以使用 context.request().isSSL
您还需要外部化您自己的 URL 以便能够在服务器端重定向到浏览器可以用来找到您的东西,您的 public URL。
首先,RoutingContext.externalizeUrl()
的 Stack Overflow 答案中有一个 Kotlin 函数,您将在此处需要它:
I have a Vertx request and I need to calculate an externally visible (public) URL
然后知道您的 public URL 您可以使用以下处理程序,它具有预期 public HTTPS 端口的默认值(默认 443 将从 URL),哪种形式的重定向(即 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
class 并查看它是否具有正确的协议和端口,如果不正确则重定向:
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()
}
}
}