如何强制 UriBuilder 使用 https?

How can I force UriBuilder to use https?

我目前 运行 Apache httpd 背后的 Dropwizard 作为反向代理,配置如下:

<VirtualHost *:443>
  <Location /api>
    ProxyPass "http://my.app.org:8080/api"
  <Location>
  ...
</VirtualHost>

使用其他 Location 服务于静态资产的设置和一些身份验证。现在,httpd 还执行 SSL 卸载,所以我的 Dropwizard 只接收纯 HTTP 请求。

在我的 Dropwizard API 中,我喜欢 return 一个 Location header 指示新创建的资源:

@Path("/comment")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
class CommentResource() {
  @PUT
  fun create(@Context uri: UriInfo, entity: EventComment): Response {
    val stored: EventComment = createEntity(entity)
    return Response.created(uri.baseUriBuilder.path(MessageStream::class.java)
            .resolveTemplate("realmish", entity.realmId)
            .path(stored.id.toString()).build()).build()
}

这将创建一个 Response,其中 Location header 来自 JerseyUriBuilder:

Location http://my.app.org/api/messages/123

在我的 SSL-only 应用程序上,自然无法加载(我真的很惊讶这并没有呈现为 http://my.app.org:8080/api/messages/123 - 可能也是 [=19= 的原因] 没有帮助)。

我知道我可以使用 baseUriBuilder.scheme("https") 强制该方案为 https,但这会重复并且很容易出错。

因此我的问题:我怎样才能使 Jersey 生成正确的 front-end URL 或成功地使 httpd 重写由 Dropwizard 生成的那些?

对于 Jersey,您可以使用 pre-matching ContainerRequestFilter 来重写 URI。例如

@PreMatching
public class SchemeRewriteFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext request) throws IOException {
        URI newUri = request.getUriInfo().getRequestUriBuilder().scheme("https").build();
        request.setRequestUri(newUri);
    }
}

然后用 Jersey (Dropwizard) 注册它

env.jersey().register(SchemeRewriteFilter.class);

编辑

以上仅在您使用 uriInfo.getAbsolutePathBuilder() 时有效。如果你想使用 uriInfo.getBaseUriBuilder(),那么你需要使用接受基本 uri 作为第一个参数的重载 setRequestUri

URI newUri = request.getUriInfo().getRequestUriBuilder().scheme("https").build();
URI baseUri = request.getUriInfo().getBaseUriBuilder().scheme("https").build();
request.setRequestUri(baseUri, newUri);

如果使用 Jetty,则可以通过在服务器上注册 org.eclipse.jetty.server.ForwardedRequestCustomizer 来避免黑客攻击。这将查看 X-Forwarded-* headers 以构建基本 URI。

使用嵌入式 Jetty 的示例:

Server jettyServer = new Server();
HttpConfiguration config = new HttpConfiguration();
config.addCustomizer(new ForwardedRequestCustomizer());
ServerConnector serverConnector = new ServerConnector(jettyServer, 
                                      new HttpConnectionFactory(config));
serverConnector.setPort(8080);
jettyServer.setConnectors(new Connector[] {serverConnector});

无论您是否在反向代理后面,这似乎都有效,所以我不知道为什么默认情况下不启用它。