啥,Tomcat里竟然還有特權(quán)應(yīng)用?
在 Tomcat 部署的應(yīng)用里,我們一般意義上都是指普通的應(yīng)用,大家可以執(zhí)行的操作等都是一樣的,一條起跑線上的,沒有區(qū)別。
但對于一些特殊的應(yīng)用,需要執(zhí)行一些特別的操作,比如官方默認(rèn)提供的 manager應(yīng)用,可以進(jìn)行應(yīng)用的啟動、停止、部署等一系列操作。這些操作的背后,是 Tomcat默認(rèn)包含的 ManagerServlet 實現(xiàn)的, manager 應(yīng)用實現(xiàn)的,是對于該 Servelet 的調(diào)用。
那我們自己寫一個應(yīng)用去調(diào)用這個 ManagerServlet 不也一樣可以嗎?
答案是不行的,至少直接操作不行,需要配置。這里就是我們今天提到的 Tomcat 內(nèi)的特權(quán)應(yīng)用(privileged app)。
manager 應(yīng)用就是自帶的一個特權(quán)應(yīng)用,因此它可以直接調(diào)用 ManagerServlet 進(jìn)行一些容器內(nèi)部的操作。
我們單獨部署的其他應(yīng)用,就會被限制調(diào)用這些容器提供的內(nèi)部組件,比如一些 Servlet、Filter、Listener。
這些限制資源進(jìn)行檢查時,分為兩種類型。
1. 一類是通過類型來進(jìn)行判斷,比如 Servlet 中,ManagerServlet、HTMLManagerServlet 這些都被稱為ContainerServlet。實現(xiàn)了相同的接口。
2. 還有一類是在配置文件中指定的,有三個文件:
- RestrictedFilters.properties
- RestrictedListeners.properties
- RestrictedServlets.properties
位于 org.apache.catalina.core 包中。
這些內(nèi)部在 DefaultInstanceManager 初始化時進(jìn)行解析,例如 servlet的內(nèi)容解析后是這些:
這些內(nèi)容也是容器提供的一些功能的實現(xiàn),比如 GCI 請求處理, JMXProxyServlet 會將 JMX 的屬性 dump 出來等等。
特權(quán)應(yīng)用的配置
那怎樣能讓單獨開發(fā)的應(yīng)用成為特權(quán)應(yīng)用,能調(diào)用這些容器的功能呢?
之前的文章介紹過應(yīng)用的部署描述文件context.xml ( Tomcat目錄部署與Context描述文件context.xml),這次我們的升級特權(quán)操作也是需要在這里進(jìn)行。
具體來說,在應(yīng)用部署時,將應(yīng)用的 Context 對象中 privileged屬性設(shè)置為 true。例如 manager 應(yīng)用的配置是這樣的,文件位于于 manager 的META-INF 目錄中,單獨開發(fā)時可以參考:
- <Context antiResourceLocking="false" privileged="true" >
這樣配置之后,在應(yīng)用部署時就會將代表應(yīng)用的 StandardContext 對象中 privileged 屬性設(shè)置為true,后面會讀取使用。
特權(quán)應(yīng)用的工作原理
應(yīng)用在啟動時,會將 DefaultInstanceManager 做為其 instanceManager。該屬性在后面 Servlet、Filter、Listener 這些組件加載時使用,做為實例管理器,進(jìn)行 newInstance的管理。
以 Servlet 為例,有些 loadOnStartup 的 Servlet,會在部署啟動應(yīng)用時直接生成實例。
在 loadServlet 階段時,會先通過 Wrapper 獲取父容器 Context 的 instanceManager,再通過 instanceManager 來加載具體的 Servlet class。
以下為 instanceManager 進(jìn)行 newInstace 時的邏輯
- public Object newInstance(String className) {
- Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
- return newInstance(clazz.newInstance(), clazz);
- }
在loadClassMaybePrivileged中,對于catalina包中的 class,會使用ServerClassLoader 來進(jìn)行加載,除了通過不同的classLoader加載外,還會進(jìn)行上面說的 privileged 檢查。
- private void checkAccess(Class<?> clazz) {
- if (privileged) {
- return;
- }
- if (Filter.class.isAssignableFrom(clazz)) {
- checkAccess(clazz, restrictedFilters);
- } else if (Servlet.class.isAssignableFrom(clazz)) {
- if (ContainerServlet.class.isAssignableFrom(clazz)) {
- throw new SecurityException("Restricted (ContainerServlet) " +
- clazz);
- }
- checkAccess(clazz, restrictedServlets);
- } else {
- checkAccess(clazz, restrictedListeners);
- }
- }
- // 這里檢查配置文件中的內(nèi)容
- private void checkAccess(Class<?> clazz, Properties restricted) {
- while (clazz != null) {
- if ("restricted".equals(restricted.getProperty(clazz.getName()))) {
- throw new SecurityException("Restricted " + clazz);
- }
- clazzclazz = clazz.getSuperclass();
- }
- }
對于 priviledged 應(yīng)用,之后的 checkAccess 就直接跳過,否則會判斷是否是 ContainerServlet,是否在配置文件中進(jìn)行限制等,只有都檢查通過才可以進(jìn)行 使用。
例如 普通的應(yīng)用在使用HTMLManagerServlet ,此時由于限制檢查,會提示500。
所以,之后有需要使用容器提供的功能時,可以將應(yīng)用升級為特權(quán)應(yīng)用,然后調(diào)用容器提供的高級功能。
【本文為51CTO專欄作者“侯樹成”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號『Tomcat那些事兒』獲取授權(quán)】