在Vertx中,我需要将所有HTTP请求重定向到相同的URL,但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编码,所以这个语言的例子都很棒!

注意: 这个问题是由作者故意编写和回答的( 自我回答问题 ),所以在SO中共享有趣问题的解决方案。

首先,如果您的代理或负载平衡器能够为您进行检查和重定向,最好是因为它具有公用URL的知识,并且是在与用户的第一次联系时更简单的过程。 但是,你也可以在服务器端做更复杂的一点。

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

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

首先,在RoutingContext.externalizeUrl()堆栈溢出答案中有一个Kotlin函数,这里需要它:
我有一个Vertx请求,我需要计算一个外部可见的(公共)URL

然后知道你的公共URL,你可以使用下面的处理程序,它具有预期的公共HTTPS端口的默认值( 默认443将从URL中消失 ),重定向的forms( 即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() } } }