【问题标题】:Tomcat behind Apache using ajp for Spring Boot applicationApache 后面的 Tomcat 使用 ajp 进行 Spring Boot 应用程序
【发布时间】:2015-01-20 04:26:37
【问题描述】:

我一直在尝试使用使用嵌入式 Tomcat 的 Spring Boot 应用程序配置 Apache Web 服务器。在 Spring Boot 之前,我曾经创建一个 ajp.conf 文件,例如:

<VirtualHost *:80>
   ServerName localhost
   <Proxy *>
      AddDefaultCharset Off
      Order deny,allow
      Allow from all
   </Proxy>

   ProxyPass /app ajp://localhost:8009/app
   ProxyPassReverse /app ajp://localhost:8009/app

 </VirtualHost>

并包含在 httpd.conf 文件中,如

Include /opt/lampp/apache2/conf/ajp.conf

并且在Tomcat的server.xml文件中,我曾经配置它监听8009端口

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" connectionTimeout="5000"

此设置有效。但是,现在使用 Spring Boot,我正在尝试使用嵌入式 tomcat 实现类似的功能。我在这里阅读了Spring Boot Documentation,并在我的 application.yml 文件中添加了以下属性:

server:
    port: 8080
    tomcat:
        remote_ip_header: x-forwarded-for
        protocol_header: x-forwarded-proto

我的 ajp.conf 文件如下所示:

<VirtualHost *:80>
   ServerName localhost
   <Proxy *>
      AddDefaultCharset Off
      Order deny,allow
      Allow from all
   </Proxy>

   ProxyPass /app ajp://localhost:8009/
   ProxyPassReverse /app ajp://localhost:8009/

 </VirtualHost>

我的spring boot tomcat配置类为

@Configuration
public class TomcatConfiguration {

private final Logger log = LoggerFactory.getLogger(TomcatConfiguration.class);

@Bean
public EmbeddedServletContainerFactory servletContainer() {
    TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
    tomcat.addAdditionalTomcatConnectors(createConnector());
    tomcat.addContextValves(createRemoteIpValves());
    return tomcat;
}

private RemoteIpValve createRemoteIpValves(){
    RemoteIpValve remoteIpValve = new RemoteIpValve();
    remoteIpValve.setRemoteIpHeader("x-forwarded-for");
    remoteIpValve.setProtocolHeader("x-forwarded-protocol");
    return remoteIpValve;
}

private Connector createConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
    connector.setScheme("ajp");
    connector.setProtocol("AJP/1.3");
    connector.setRedirectPort(8443);
    //connector.setSecure(true);
    connector.setPort(8009);
    return connector;
}

在我的 apache 错误日志中,我看到:

AH01080: ajp_msg_check_header() got bad signature 4854
[proxy_ajp:error] [pid 24073] AH01031: ajp_ilink_receive() received bad header
[proxy_ajp:error] ajp_read_header: ajp_ilink_receive failed
[proxy_ajp:error] (120007)APR does not understand this error code: [client xx.xx.xx.xx:60916] AH00878: read response failed from (null) (*)

不确定这里发生了什么。我在网上搜索了很多,但找不到关于如何使用 spring boot 应用程序在 apache 后面提供 tomcat 的好的文档。最后,我也想对多个 tomcat 实例进行负载平衡。

【问题讨论】:

标签: apache tomcat7 spring-boot ubuntu-14.04 ajp


【解决方案1】:

可通过属性或 yml 文件进行配置。

@Configuration
@ConfigurationProperties(prefix = "tomcat")
public class TomcatConfiguration {
   private int ajpPort = 8009;

   private boolean ajpAllowTrace = false;
   private boolean ajpSecure = false;
   private String ajpScheme = "http";
   private boolean ajpEnabled;


  @Bean
  public EmbeddedServletContainerFactory servletContainer() {

    TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
    if (isAjpEnabled()) {
        Connector ajpConnector = new Connector("AJP/1.3");
        ajpConnector.setProtocol("AJP/1.3");
        ajpConnector.setPort(getAjpPort());
        ajpConnector.setSecure(isAjpSecure());
        ajpConnector.setAllowTrace(isAjpAllowTrace());
        ajpConnector.setScheme(getAjpScheme());
        tomcat.addAdditionalTomcatConnectors(ajpConnector);
    }

    return tomcat;
    }
// ... Get/Set
}

application.yml

tomcat:
  ajpEnabled: true
  ajpPort: 9009
  ...

【讨论】:

    【解决方案2】:

    遇到了类似的问题,但使用的是 HTTP 代理。在对 Spring Boot 1.3 进行了一些调试后,我找到了以下解决方案。 AJP 代理应该类似。

    1.您必须在 Apache 代理上设置标头:

    <VirtualHost *:443>
        ServerName www.myapp.org
        ProxyPass / http://127.0.0.1:8080/
        RequestHeader set X-Forwarded-Proto https
        RequestHeader set X-Forwarded-Port 443
        ProxyPreserveHost On
        ... (SSL directives omitted for readability)
    </VirtualHost>
    

    2. 您必须告诉您的 Spring Boot 应用程序使用这些标头。所以把下面这行放在你的 application.properties 中(或者 Spring Boots 理解属性的任何其他地方):

    server.use-forward-headers=true
    

    如果您正确执行这两件事,您的应用程序发送的每个重定向都将不会转到http://127.0.0.1:8080/[path],而是自动转到https://www.myapp.com/[path]

    更新 1。 关于此主题的文档是 here。您至少应该阅读它以了解属性 server.tomcat.internal-proxies,它定义了可信任的代理服务器的 IP 地址范围。

    【讨论】:

    • 不错的补充;这也是我所达到的。一切都很好,有一个小问题,当我使用 spring hateoas ControllerLinkBuilder.linkTo + methodOn 我生成的链接以 https 开头,但还包含端口号 443。当我使用 BasicLinkBuilder 时,端口不会添加到 URL .对此有什么想法吗?
    • @tim 从未使用过这些课程。您可以尝试调试到 ContollerLinkBuilder 和 BasicLinkBuilder。如果他们有不同,您可以向 Spring 人员发布错误报告。他们反应很快。
    【解决方案3】:

    从上面的cmets推导出来:

    @Configuration
    public class TomcatAjpConfig {
    
    @Bean
    @SuppressWarnings("static-method")
    public EmbeddedServletContainerFactory servletContainer() {
        TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
        tomcat.addAdditionalTomcatConnectors(createConnector());
        tomcat.addContextValves(createRemoteIpValves());
        return tomcat;
    }
    
    private static RemoteIpValve createRemoteIpValves() {
        RemoteIpValve remoteIpValve = new RemoteIpValve();
        remoteIpValve.setRemoteIpHeader("x-forwarded-for");
        remoteIpValve.setProtocolHeader("x-forwarded-proto");
        return remoteIpValve;
    }
    
    private static Connector createConnector() {
        Connector connector = new Connector("AJP/1.3");
        connector.setPort(8009);
        return connector;
    }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-09
      • 1970-01-01
      • 2016-07-01
      • 2010-10-31
      • 1970-01-01
      • 2020-08-14
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多