Apache 后面的 Tomcat 使用 ajp 进行 Spring Boot 应用程序
Posted
技术标签:
【中文标题】Apache 后面的 Tomcat 使用 ajp 进行 Spring Boot 应用程序【英文标题】:Tomcat behind Apache using ajp for Spring Boot application 【发布时间】: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 实例进行负载平衡。
【问题讨论】:
docs.spring.io/autorepo/docs/spring-boot/1.0.0.RC4/api/org/… 或 docs.spring.io/spring-boot/docs/current/reference/html/… . 为什么要创建 HTTP NIO 连接器?您需要创建 AJP 连接器 -new Connector("AJP/1.3")
。
@PavelHoral 你把我推向了正确的方向。当我使用 org.apache.coyote.ajp.AjpProtocol 连接器时,它起作用了。不确定,我如何做到这一点是最好的方法。但是,它现在有效。非常感谢!
【参考方案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 hateoasControllerLinkBuilder.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;
【讨论】:
以上是关于Apache 后面的 Tomcat 使用 ajp 进行 Spring Boot 应用程序的主要内容,如果未能解决你的问题,请参考以下文章
Apache + Tomcat:使用 mod_proxy 代替 AJP
Apache 2.4 代理 AJP 使用 Tomcat 8 服务多个域
(CVE-2020-1938)Apache Tomcat AJP文件包含漏洞复现