0x00 核心配置文件 Server.xml
位于目录 conf 中的配置文件 server.xml 是 Tomcat 的核心配置文件,无论是涉及到 Tomcat 的性能还是安全,都是在此文件中进行配置。
server.xml 由比较多的组织部件组成,下面是一张来自 Tomcat 使用文档中的图片,能够清晰地表达出各个配置部件的关系。
如果能够理解上面这图示的话,基本上对 Tomcat 的配置可以做到“胸有成竹”,下面是上图的一些注解:
-
Server 上的 8005 端口是 ShutDown 端口,通常用来接收 Server 启动、重启之类的命令。
-
Connector 上的连接(通讯端口),包括有两个:
-
8080 :web 请求处理端口
-
8009 : JK 集群通讯端口(通常与 Apache 集成)
-
-
从上到下来看部件关系:
-
每个服务器(Tomcat)可以有多个 Service 进程。通过配置不同的 Service 进程,可以使用不同访问端口来分离不同的应用。
-
每个 Service 进程可以配置多个 Connector,每个 Connector 对应一个端口,这意味着可以设置多个访问端口(访问内容一样)。例如,我们配置 80、8080 两个端口。
-
每个 Service 进程可以配置多个 Engine (每个 Engine 相当于一个 Servlet 容器),但是只有最后那个才生效,所以通常只会配置一个 Engine。
- Engine 相当于请求处理器,接收来自 Connector 的请求数据,然后进行分析处理,再分派到 Host 中处理。
-
每个 Engine 可以配置多个 Host (虚拟主机)
-
每个 Host 可以配置多个 Context (应用)
-
-
有几个配置注意点
0x01 配置实战
下面以 Tomcat 8.x 为例子进行配置,虽然没试过 Tomcat 7.x。不过,相信应该同样适合。
- 为每个 Servlet 容器分配独立的访问端口(或者为每个应用分配置独立的访问端口)
下面是配置图示:
有时为了方便设置控制策略,会将系统分成多个应用,并且通过不同的端口进行访问。本例子,假设系统分成“前台”和“后台管理”两部分,不同子系统使用不同的访问端口。为了实现这样的需求,我们就定义为两个不用服务,每个服务配置自己的访问端口。 下面是配置示例:
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="FrontEnd"> <!-- 前端应用 -->
<!-- 前端应用使用 80 这个端口 -->
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-->
<!-- Engine name 要与 Service name 一致 -->
<Engine name="FrontEnd" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<!-- 前端应用依然放在 webapps 这个目录 Host Name 通常与 Engine defaultHost 一致-->
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<!-- access log 也用不同的文件名称来收集 -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="frontend_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
<Service name="BackEnd"> <!-- 后台管理 -->
<!-- 后台管理使用 8080 这个端口 -->
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Engine name="BackEnd" defaultHost="localhost">
<!-- 后端应该存放目录为:BackEndApps 要分开目录存放 -->
<Host name="localhost" appBase="BackEndApps" unpackWARs="true" autoDeploy="true">
<!-- access log 也用不同的文件名称来收集 -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="backend_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
于是呢,我们就可以使用 http://xxxx/index 通过 80 端口访问前端应用,但是访问不了后端的应用。后端的应用需要类似 http://xxxx:8080/admin/index 来访问。
因为是不同端口访问,网络安全工程师就可以使用控制策略来,让后台应用只能由局域网 ip 访问,提高系统的安全性。
- 使用不同端口访问同一个应用
这个需求似乎有点“怪”,不过有时候我们希望用户通过 80 和 8080 都能够访问应用。习惯了 Tomcat 的 8080 端口之后,需要配置使用 80 端口,那就干脆两个端口一起用吧。
下面是配置图示:
有了第一个示例的经验,看了图示应该很快配置出来:
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<!-- 使用 80 端口访问 -->
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<!-- 配置使用 8080 端口访问 -->
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-->
<!-- Engine name 要与 Service name 一致 -->
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<!-- 应用依然放在 webapps 这个目录 Host Name 通常与 Engine defaultHost 一致-->
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
配置好之后,重启 Tomcat ,你就会发现使用 80 或 8080 都能访问同一应用了。例如 http://xxxx/myapp/index 和 http://xxxx:8080/myapp/index 都能访问。
- 配置多个虚拟主机(即通过不同 ip 或 域名 访问不同的应用)
这个跟上面的不同端口访问不同应用不一样,这里是同一个端口,但是通过不同的 ip (如果可以设置多个的话)或者不同的域名来访问不同的应用。
下面是配置图示:
也就是在 Engine 下面猛加 Host 配置。要注意,不同的主机对应的目录应该是不同的,弄到同一个目录就没意思了。
下面是配置示例:
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<!-- 配置使用 8080 端口访问 -->
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-->
<!-- Engine 的 defaultHost 是默认访问,而 Host 中并不需要一定存在这个默认的虚拟主机 -->
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<!-- www.me.com 的虚拟主机 -->
<Host name="www.me.com" appBase="d:\webapp\www.me.com\webapps" workDir="d:\webapp\www.me.com\works" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="me.com_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
<!-- www.you.com 的虚拟主机 -->
<Host name="www.you.com" appBase="d:\webapp\www.you.com\webapps" workDir="d:\webapp\www.you.com\works" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="you.com_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
要留意的是,我这个示例“故意”没有配置一个 defaultHost,这是允许的。例子中,为不同的“域名”定义不同的应用目录和工作目录,不同域名目录之间是互不干涉的,也就是说使用“www.me.com”的域名是无法访问“www.you.com”下的应用,这也是虚拟主机的目的。
- 还有更复杂的吗?
可以有更复杂的配置吗?试一试下面的配置图:
0x10 结束语
作为一个 Web 服务 + Servlet 容器服务器,Tomcat 本身已经提供了足够用的功能。虽然从 Web 服务器的角度来看,Tomcat 因为不是主业而偏弱,不过我们仍然能够通过其他途径来弥补其不足。一方面,众所周知 Tomcat 可以跟 Apache 整合变强;但另一方面,Tomcat 也提供足够灵活的配置与开发模式,让专业人士添加更多的功能。例如,通过 IP 识别限制访问的客户端;请求过滤等等。不过,这些并不是本篇文章所叙述的内容,有机会另起篇幅来描述。