1. 场景 后端存在N个tomcat实例,前端通过nginx反向代理和负载均衡。 tomcat1 tomcatN | | | | ----------------- | nginx 2. 需求 为了保护后端应用,tomcat实例只允许前端nginx服务器IP访问,其他任何地址的访问都被拒绝。
3. 实现 编辑${TOMCAT_HOME}/conf/server.xml,添加org.apache.catalina.valves.RemoteAddrValve配置,如下所示:
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<!-- 限制指定IP地址访问Tomcat -->
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127.0.0.1"
deny=""/>
</Host>
4. 原理
(1)概述 在tomcat中存在一个与容器关联的组件Valve,该组件用于处理request请求。源码注释为:
详见tomcat请求处理时序图:http://tomcat.apache.org/tomcat-8.5-doc/architecture/requestProcess/request-process.png。 因此,我们可以通过添加指定Valve实现,用于在处理request请求时实现相应功能。 例如:在这里通过在<Host>元素中添加org.apache.catalina.valves.RemoteAddrValve实现限制指定IP地址访问应用程序。 (2)Valve类图
(3)源码解读
org.apache.catalina.valves.RemoteAddrValve实现Valve处理request请求接口:
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
String property;
if (addConnectorPort) {
property = request.getRequest().getRemoteAddr() + ";" + request.getConnector().getPort();
} else {
property = request.getRequest().getRemoteAddr();
}
process(property, request, response);
}
org.apache.catalina.valves.RequestFilterValve process实现:
protected void process(String property, Request request, Response response)
throws IOException, ServletException {
if (isAllowed(property)) {
getNext().invoke(request, response);
return;
}
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("requestFilterValve.deny",
request.getRequestURI(), property));
}
// Deny this request
denyRequest(request, response);
}
public boolean isAllowed(String property) {
// Use local copies for thread safety
Pattern deny = this.deny;
Pattern allow = this.allow;
// Check the deny patterns, if any
if (deny != null && deny.matcher(property).matches()) {
return false;
}
// Check the allow patterns, if any
if (allow != null && allow.matcher(property).matches()) {
return true;
}
// Allow if denies specified but not allows
if (deny != null && allow == null) {
return true;
}
// Deny this request
return false;
}