1.前言
最近在项目中有一个想法,要是项目中能通过网页热部署一些二次开发的模块,那应该是一件很有意思而且很有实际作用的功能。所以想到了Tomcat中类加载器中反委派机制做法。为了更深入了解tomcat类加载器的反委派机制的具体实现,需要搭建Tomcat源码环境。虽然不是很难,但是还有避免不了一些坑,依次记载,方便大家学习交流。
2.环境准备
2.1 Idea环境
本次Idea版本采用比较稳定的IntelliJIDEA2018.2.1。
2.2 JDK版本
JDK版本为1.8.0.131_x64。
2.3 Tomcat版本
本次tomcat版本采用tomcat7.0.103。可以通过在官网下载。注意,此处应该选择Source Code Distributions。下载的格式为zip。
https://tomcat.apache.org/download-70.cgi
3.源码部署
3.1 解压zip压缩包,此处简单,一句带过。
3.2 通过idea导入此项目
依次选择【Open -->选择刚才解压的文件夹】。
3.3 指定jdk,工程语言级别以及编译输出路径
3.4 指定src以及test路径
将java指定为【Sources Root】,将test指定为【Test Sources Root】,具体操作如下:
3.5 引入Jar包
此时,我们发现有部分代码飘红,说明需要引入部分jar包,如下图。
为了方便大家构建项目,这里我们通过maven将依赖的jar包引导到工程中去。下文是我整理好的pom.xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.surpass</groupId>
<artifactId>tomcat</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>tomcat</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.7</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.core</artifactId>
<version>3.20.0</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxrpc</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jmock</groupId>
<artifactId>jmock</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>4.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.4.2</version>
</dependency>
</dependencies>
</project>
3.5.1 将pom.xml文件拷贝到tomcat的文件夹下。
3.5.2 编程maven工程,引入pom.xml文件
选择idea右侧的选项卡【Maven Projects】,然后点击【+】,现在刚才拷贝进去的pom.xml文件,在弹出的点击【import changes】,此操作对于熟悉idea的读者并不陌生,可以直接略过。
此时,我们发现之前飘红的已经消失了。
3.6 运行tomcat启动文件
我们通过路径找到类文件org.apache.catalina.startup.Bootstrap。熟悉idea的朋友知道,双击shift键,输入Bootstrap可以快速定位。接下来我们运行此类。很不幸,发现缺少一个类。
通过此类分析,这个类并没有在import导入,所以我们猜想这个CookieFilter类应该和TestCookieFilter在同一个全类路径,即在util包下。为了方便大家学习,我也把这个文件分享出来,然后把此类放到${tomcat}\test\util下。
/*
* 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 util;
import java.util.Locale;
import java.util.StringTokenizer;
/**
* Processes a cookie header and attempts to obfuscate any cookie values that
* represent session IDs from other web applications. Since session cookie names
* are configurable, as are session ID lengths, this filter is not expected to
* be 100% effective.
*
* It is required that the examples web application is removed in security
* conscious environments as documented in the Security How-To. This filter is
* intended to reduce the impact of failing to follow that advice. A failure by
* this filter to obfuscate a session ID or similar value is not a security
* vulnerability. In such instances the vulnerability is the failure to remove
* the examples web application.
*/
public class CookieFilter {
private static final String OBFUSCATED = "[obfuscated]";
private CookieFilter() {
// Hide default constructor
}
public static String filter(String cookieHeader, String sessionId) {
StringBuilder sb = new StringBuilder(cookieHeader.length());
// Cookie name value pairs are ';' separated.
// Session IDs don't use ; in the value so don't worry about quoted
// values that contain ;
StringTokenizer st = new StringTokenizer(cookieHeader, ";");
boolean first = true;
while (st.hasMoreTokens()) {
if (first) {
first = false;
} else {
sb.append(';');
}
sb.append(filterNameValuePair(st.nextToken(), sessionId));
}
return sb.toString();
}
private static String filterNameValuePair(String input, String sessionId) {
int i = input.indexOf('=');
if (i == -1) {
return input;
}
String name = input.substring(0, i);
String value = input.substring(i + 1, input.length());
return name + "=" + filter(name, value, sessionId);
}
public static String filter(String cookieName, String cookieValue, String sessionId) {
if (cookieName.toLowerCase(Locale.ENGLISH).contains("jsessionid") &&
(sessionId == null || !cookieValue.contains(sessionId))) {
cookieValue = OBFUSCATED;
}
return cookieValue;
}
}
3.7 运行tomcat启动文件
此时我们在运行此类,发现运行成功。随后我们在网页打开http://127.0.0.1:8080验证是否启动成功。
4 修改乱码
不知道上面读者是否发现在输出日志的控制台中发生了乱码。
其实解决问题的思路很简单,我们需要修改两个类。
4.1 org.apache.tomcat.util.res.StringManager
代码如插入位置如下图
try{
value = new String(value.getBytes("ISO-8859-1"),"UTF-8");
}catch (Exception e){}
4.2 org.apache.jasper.compiler.Localizer
代码如插入位置如下图
try{
errMsg = new String(errMsg.getBytes("ISO-8859-1"),"UTF-8");
}catch (Exception e){}
4.3 此时我们再次运行,发现乱码不见了。