【问题标题】:SSL Endpoint with Heroku and 303使用 Heroku 和 303 的 SSL 端点
【发布时间】:2014-12-30 05:42:58
【问题描述】:

我已经使用 heroku 创建了一个 SSL 端点。我有一个测试环境和一个实时环境。我有一个生成 303 的 REST 调用。由于 Heroku 在其路由器中处理 SSL,我不确定如何检测我的 SEE OTHER URL 是否应该创建基于 HTTP 或 HTTPS 的 URI。下面是一些示例代码:

@GET
@Path( "/job/{jobId}" )
public Response getCallStatus( @PathParam( "jobId" ) Long jobId, @Context UriInfo uriInfo ) throws Exception {
    if ( !jobService.isDone( jobId ) )
        return build( Response.ok( POLLING_FREQUENCY ) );

    URI jobLocation = uriInfo.getAbsolutePathBuilder().path( "result" ).build();
    return build( Response.seeOther( jobLocation ) );
}

因为我的服务器没有处理 SSL(heroku 是),所以 REST 调用的绝对路径将使用 HTTP 而不是 HTTPS。如果我硬编码 HTTPS,我将破坏我的单元测试或其他不需要 HTTPS 协议的环境。

有什么想法吗?还是我误解了 heroku 是如何做到这一点的?

【问题讨论】:

    标签: rest ssl heroku


    【解决方案1】:

    好的,这就是答案。 Heroku 不会将请求作为 HTTPS 转发。因此,您需要查看 x-fowarded-proto 标头来决定您应该将 303 位置发送回客户端。上面的代码示例会变成这样:

    @GET
    @Path( "/job/{jobId}" )
    public Response getCallStatus( @PathParam( "jobId" ) Long jobId, @Context UriInfo uriInfo, @Context HttpHeaders headers ) throws Exception {
        if ( !jobService.isDone( jobId ) )
            return build( Response.ok( POLLING_FREQUENCY ) );
    
        UriBuilder builder = uriInfo.getAbsolutePathBuilder().path( "result" );
        String scheme = headers.getHeaderString( "x-forwarded-proto" );
        if ( scheme != null )
            builder.scheme( scheme );
    
        return build( Response.seeOther( builder.build() ) );
    }
    

    基本上就是这样。

    但是,一种不需要对编码的 REST METHOD 进行任何更改的更好的处理方法是添加一个容器请求过滤器,如下所示:

    @PreMatching
    public class HerokuContainerRequestFilter implements ContainerRequestFilter {
    
        @Override
        public void filter( ContainerRequestContext ctx ) throws IOException {
            List<String> schemes = ctx.getHeaders().get( "x-forwarded-proto" );
            if ( schemes != null && !schemes.isEmpty() ) {
                String scheme = schemes.get( 0 );
                UriBuilder builder = ctx.getUriInfo().getRequestUriBuilder();
                ctx.setRequestUri( builder.scheme( scheme ).build() );
            }
        }
    }
    

    然后你只需像这样用你的 RestConfig 注册这个过滤器:

    public class RestApplication extends ResourceConfig {
    
        public RestApplication() {
    
            packages( "com.myapp.rest.service" );
    
            // along with any other providers, etc that you register
            register( HerokuContainerRequestFilter.class );
        }
    }
    

    【讨论】:

      【解决方案2】:

      虽然 user1888440 回答完全有效,但我宁愿在服务器级别配置 https 转发。

      例如,如果您使用嵌入式码头作为 heroku Web 服务器,则可以使用码头内置 org.eclipse.jetty.server.ForwardedRequestCustomizer :

      自定义代理转发请求。

      此定制程序查看 HTTP 请求以获取表明它已被一个或多个代理转发的标头。具体处理有:

      • X 转发主机
      • X 转发服务器
      • X-Forwarded-For
      • X-Forwarded-Proto

      如果存在这些标头,则更新请求对象,以便代理不会被视为请求所经过的连接的另一个端点

      所以不要用 :

      来启动你的服务器
      Server server = new Server(port);
      

      你可以使用:

          Server server = new Server();
          HttpConfiguration httpConfiguration = new HttpConfiguration();
          httpConfiguration.addCustomizer(new ForwardedRequestCustomizer());
          ServerConnector serverConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
          serverConnector.setPort(port);
          server.addConnector(serverConnector);
      

      【讨论】:

        猜你喜欢
        • 2016-05-14
        • 1970-01-01
        • 1970-01-01
        • 2012-07-10
        • 1970-01-01
        • 1970-01-01
        • 2012-06-04
        • 2012-12-26
        • 2013-06-10
        相关资源
        最近更新 更多