前言
tomcat中有两个valve,RemoteHostValve 和 RemoteAddrValve,但是这两个同时使用会有问题(也可能是我不会用)。因为我希望在所有地方用域名可以访问,但是只有在公司内才能用ip访问。所以它的配置无法实现我的想法。于是想到了自己修改源码后编译即可。
下载
所有文件下载地址:http://pan.baidu.com/s/1dDv2Ewp
1. 下载 tomcat7 源码(需要jdk1.6,高低都不行)
2. 下载安装 jdk1.6 (我的是mac,坑啊,好难找,文后会有下载地址)
3. 下载 ant
配置
- 解压tomcat7源码到目录
- 解压ant到目录
- 安装jdk,jdk1.6在所有平台都是双击运行(运行安装),如果此前你有其它版本的jdk,请注意修改好环境变量。
- 把ant目录下的bin目录添加到环境变量path中,linux例:export PATH=$PATH:/usr/local/ant-1.9.6/bin
- 进入到tomcat7源码,复制一份build.properties.default到 build.properties,修改其中的 base.path=/usr/share/java 为一个有权限修改的目录(linux 和 mac 下可能无权限读写)
- 修改了环境变量记得 source /etc/profile 一下,以使生效。
- 在tomcat源码目录下 运行 ant,结果如下:
Buildfile: /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/build.xml
build-prepare:
[delete] Deleting directory /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/temp
[delete] Deleting directory /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/work
[mkdir] Created dir: /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/temp
download-compile:
testexist:
[echo] Testing for /Users/yiboliu/IdeaProjects/java/tomcat-native-1.1.33/tomcat-native.tar.gz
downloadfile-2:
proxyflags:
setproxy:
testexist:
[echo] Testing for /Users/yiboliu/IdeaProjects/java/commons-daemon-1.0.15/commons-daemon-1.0.15.jar
downloadgz-2:
testexist:
[echo] Testing for /Users/yiboliu/IdeaProjects/java/commons-daemon-1.0.15/commons-daemon-1.0.15-native-src.tar.gz
downloadfile-2:
proxyflags:
setproxy:
testexist:
[echo] Testing for /Users/yiboliu/IdeaProjects/java/commons-pool-1.5.7-src/build.xml
downloadgz-2:
proxyflags:
setproxy:
testexist:
[echo] Testing for /Users/yiboliu/IdeaProjects/java/commons-dbcp-1.4-src/build.xml
downloadgz-2:
build-prepare:
[delete] Deleting directory /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/temp
[mkdir] Created dir: /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/temp
build-manifests:
build-tomcat-dbcp:
testexist:
[echo] Testing for /Users/yiboliu/IdeaProjects/java/ecj-4.4.2/ecj-4.4.2.jar
downloadfile-2:
compile-prepare:
download-validate:
validate:
compile-java6:
guess-java7:
check-java7:
compile-java7:
compile:
build-manifests:
package-java7:
package:
build-docs:
[xslt] Transforming into /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/webapps/docs
[xslt] Transforming into /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/webapps/docs/appdev
[xslt] Transforming into /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/webapps/docs/funcspecs
[xslt] Transforming into /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/webapps/docs/config
[xslt] Transforming into /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/webapps/docs/architecture
[xslt] Transforming into /Users/yiboliu/IdeaProjects/apache-tomcat-7.0.65-src/output/build/webapps/docs/tribes
compile-webapp-examples:
deploy:
examples-sources:
[echo] Building Tomcat JDBC pool libraries
prepare:
download:
build-src:
build:
BUILD SUCCESSFUL
Total time: 2 seconds
说明运行编译成功,编译好的tomcat在 ./output/build/ 下面。
可能的问题
jdk版本问题
错误日志
BUILD FAILED build.xml:2361: The following error occurred while executing this line:
BUILD FAILED build.xml:2449: Compile failed; see the compiler error output for details.
error: DelegatingPreparedStatement is not abstract and does not override abstract method isCloseOnCompletion()
如上,如果遇到类没有实现什么方法,但是它又不是一个抽象类云云的问题,基本可以确定是jdk版本,修改环境变量为1.6版本的即可。
没遇到其它问题
- 无内容
修改源码
这才是本文的重点
复制RemoteHostValve类为新的类
此类用于既能够匹配host,也能匹配address。
内容如下:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.valves;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import javax.servlet.ServletException;
import java.io.IOException;
/**
* Concrete implementation of <code>RequestFilterValve</code> that filters
* based on the remote client's host name optionally combined with the
* server connector port number.
*
* @author yibo.liu
*/
public final class RemoteHostAddrValve
extends RequestFilterValve {
// ----------------------------------------------------- Instance Variables
/**
* The descriptive information related to this implementation.
*/
private static final String info =
"org.apache.catalina.valves.RemoteHostAddrValve/1.0";
/**
* Flag deciding whether we add the server connector port to the property
* compared in the filtering method. The port will be appended
* using a ";" as a separator.
*/
protected volatile boolean addConnectorPort = false;
// ------------------------------------------------------------- Properties
/**
* Return descriptive information about this Valve implementation.
*/
@Override
public String getInfo() {
return (info);
}
/**
* Get the flag deciding whether we add the server connector port to the
* property compared in the filtering method. The port will be appended
* using a ";" as a separator.
*/
public boolean getAddConnectorPort() {
return addConnectorPort;
}
/**
* Set the flag deciding whether we add the server connector port to the
* property compared in the filtering method. The port will be appended
* using a ";" as a separator.
*
* @param addConnectorPort The new flag
*/
public void setAddConnectorPort(boolean addConnectorPort) {
this.addConnectorPort = addConnectorPort;
}
// --------------------------------------------------------- Public Methods
/**
* Extract the desired request property, and pass it (along with the
* specified request and response objects) to the protected
* <code>process()</code> method to perform the actual filtering.
* This method must be implemented by a concrete subclass.
*
* @param request The servlet request to be processed
* @param response The servlet response to be created
* @throws IOException if an input/output error occurs
* @throws ServletException if a servlet error occurs
*/
@Override
public void invoke(Request request, Response response)
throws IOException, ServletException {
String host;
if (addConnectorPort) {
host = request.getRequest().getRemoteHost() + ";" + request.getConnector().getPort();
} else {
host = request.getRequest().getRemoteHost();
}
String addr = request.getRequest().getRemoteAddr();
process(host, addr, request, response);
}
/**
* {@link #process(String, Request, Response)}
*
* @param host
* @param addr
* @param request
* @param response
* @throws IOException
* @throws ServletException
*/
protected void process(String host, String addr, Request request, Response response)
throws IOException, ServletException {
if (isAllowed(host)) {
getNext().invoke(request, response);
return;
} else if (isAllowed(addr)) {
getNext().invoke(request, response);
return;
}
// Deny this request
denyRequest(request, response);
}
}
说明
在 invoke 方法中同时获取 host 和 address ,传给新的方法 process ,此方法来自于父类,但是稍作修改。
添加配置
在文件 mbeans-descriptors.xml 中,找到 RemoteHostValve ,将其配置文件复制一份,并修改新的类名为 RemoteHostAddrValve 即可。
再编译
在命令行中,进入tomcat 源码目录,执行 ant。 编译成功后,即可测试。
测试
在./output/build/conf/server.xml 中的Host 里面添加
这里的ip是我本地ip地址,域名是我在hosts文件中配置的域名,这里没有使用 localhost 和 127.0.0.1,目的是为了让它们两个不能访问。
运行 ./output/build/bin/startup.sh 启动 tomcat ,即可看到结果,10.0.0.137:8080 和 test.test.com:8080 均可访问,但是 localhost:8080 和 127.0.0.1:8080 却不可以,测试成功。
另注
如果 request.getRemoteHost() 不能获取到域名,请检查tomcat配置文件,server.xml 中的 Connector 连接中是否设置了 enableLookups,此值应该设置为 true ,因为即为 true ,设置为 false 时,只能获取到 ip地址,不能获取到 域名信息。
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
enableLookups="true"
redirectPort="8443" />