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; }