天天看點

Tomcat 7 的七大新特性(轉)

Tomcat 7引入了許多新功能,并對現有功能進行了增強。很多文章列出了Tomcat 7的新功能,但大多數并沒有詳細解釋它們,或指出它們的不足,或提供代碼示例。本文将明确描述Tomcat 7中七個最顯著的特征和新增的功能,并對其作出評論,而不是僅僅列出新的功能。本文還提供了代碼例子以友善你可以對其有更好的了解。

本文分為兩個部分,分别是“Tomcat 7的新特性”和“Tomcat 7增強的功能”。

Tomcat 7新特性

1. 使用随機數去防止跨站腳本攻擊;

2. 改變了安全認證中的jessionid的機制,防止session攻擊;

3. 記憶體洩露的偵測和防止;

4. 在war檔案外使用别名去存儲靜态内容;

Tomcat 7的增強功能

5 對Servlet 3.0,JSP 2.2和JSP-EL 2。2的支援;

6 更容易将Tomcat内嵌到應用去中去,比如JBoss;

7 異步日志記。

根據Mark Thomas,Tomcat 7委員會的經理的說法,Tomcat 7最顯著的三個特征是Servlet 3.0,記憶體檢測洩露和增強的安全特性。

Tomcat 7的例子程式中,包含了Eclipse的工程檔案和Ant的建構檔案,以友善去建構war檔案。其中Eclipse工程檔案有例子代碼描述了Tomcat 7的一些新特性。

下面逐一開始介紹。

Tomcat 7新特性

1. 使用随機數去防止跨站請求僞造攻擊。

Wikipedia将跨站請求僞造攻擊(Cross Site Request forgery,CSRF)定義為:“一種影響Web應用的惡意攻擊。CSRF讓使用者當進入一個可信任的網頁時,被強行執行惡意代碼。

經典的防止CSRF攻擊的方法是使用随機數的方式,Wikipedia中定義為“利用随機或僞随機數嵌入到認證協定中,以確定舊的不能在以後的重播攻擊中被利用。”

Tomcat 7中有一個servlet過濾器,用于将随機數存儲在使用者每次請求處理後的seesion會話中。這個随機數,必須作為每次請求中的一個參數。 Servlet過濾器然後檢查在請求中的這個随機數是否與存儲在使用者session中的随機數是一樣的。如果它們是相同的,該請求是判斷來自指定的網站。如果它們是不同的,該請求被認為是從其他網站發出并且會被拒絕。

這個servlet過濾器是十分簡單的,下面是從Tomcat 源代碼CsrfPreventionFilter文檔中摘錄的片段:

Java代碼:

  1. public class CsrfPreventionFilter extends FilterBase {       
  2. public void doFilter(ServletRequest request, ServletResponse response,       
  3. FilterChain chain) throws IOException, ServletException {       
  4. String previousNonce = req.getParameter(Constants.CSRF_NONCE_REQUEST_PARAM);       
  5. String expectedNonce = (String) req.getSession(true).getAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME);       
  6. if (expectedNonce != null && !expectedNonce.equals(previousNonce)) {       
  7. res.sendError(HttpServletResponse.SC_FORBIDDEN);       
  8. return;       
  9. }       
  10. String newNonce = generateNonce();       
  11. req.getSession(true).setAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME, newNonce);     

是以每個URL位址中都有一個從使用者session中提取的随機數,下面是使用的JSTL例子:

在以前,JSTL中構造連結可以這樣:

  1. < c:url var="url" value="/show" >   
  2. < c:param name="id" value="0" / >   
  3. < /c:url >   
  4. < a href="${show}" >Show< /a >  

而現在可以這樣:

  1. < c:url var="url" value="/show" >   
  2. < c:param name="id" value="0" / >   
  3. < c:param name="org.apache.catalina.filters.CSRF_NONCE" value="${session.org.apache.catalina.filters.CSRF_NONCE}" / >   
  4. < /c:url >  

具體的例子可以參考Tomcat 7自帶例子中的示範,這個過濾器可以在web.xml中進行配置,配置後,所有通路如http://localhost:8080/Tomcat7demo/csrf/ 的都必須帶上參數,不帶上參數的話會出現403禁止通路錯誤。

當然這種方法的缺點就是所有的連結都必須帶上這個随機數。

2. 改變了安全認證中的jessionid的機制,防止session攻擊。

Session劫持攻擊通常是以下的情況:

1 惡意攻擊者先通路一個網頁,由于cookie是以jsession id的方式存儲在浏覽器中的,即使攻擊者不登陸,他可以僞造一個帶有jsession id的位址,把它發給受害者,比如:http://example.com/login?JESSIONID=qwerty);

2 受害者點這個帶有jsessionid的連結,提示輸入驗證資訊之後就登陸系統;

3 攻擊者現在使用這個帶jsessionid的連結,以受害者的身份登陸進系統了。

對于攻擊者來說,将jsessionid加在url中以及通過一個惡意表單發送出去是很容易的事,對于session劫持攻擊的更較長的描述,請參考Acros Security組織的白皮書“Session Fixation Vulnerability in Web-based Applications”。

Tomcat 7對此的解決方案是一個更新檔,它在驗證後改變了jsessionid。這個更新檔主要是應用在Tomcat 7中,當然在Tomcat 5和6中也可以使用但隻是有些不同。

根據Mark Thomas說的,應用了Tomcat 7的這個更新檔後:

• Tomcat預設情況下安全性不再變得脆弱,因為驗證後會話發生了變化

• 如果使用者改變了預設設定(比如應用程式不能處理變化了的session id),風險也會降到最小,因為在Servlet 3中,可以禁止在url中進行會話跟蹤。

而在Tomcat 5和Tomcat 6中,應用了更新檔後:

• 能阻止session劫持攻擊,因為能讓Tomcat在驗證後改變session id。

• 如果應用程式不能處理變化了的session id,可以通過寫自定義的過濾器去檢查request.isRequestedSessionIdFromURL()和其傳回的結果,以降低風險。

以上這些改變都是Tomcat在幕後所做的,開發者根本不用去理會。

3. 記憶體洩露的偵測和防止

開發者在部署他們寫的程式到生産環境上時,經常會遇到Pemgen錯誤:OutOfMemoryError。這是由于記憶體洩露而引起的。通常開發者是通過增大permgen記憶體的大小去解決或者就是重新啟動Tomcat。

Tomcat 7包含了一個新的特性,它通過把不能垃圾回收的引用對象移走的方法,能解決一些Permgen記憶體洩露的問題。這個特性對程式員部署應用程式在他們的開發環境中是十分友善的,因為程式員在開發環境中為了節省時間一般不重新啟動Tomcat就能部署新的war檔案。在生産環境中,最好的建議還是停掉Tomcat,然後清除work下面的目錄檔案并且重新部署應用。

當然,記憶體洩露檢測和防止這個特性現在還不是很完善,還是有的情況Tomcat不能檢測記憶體洩露和修複之的,是以對于生産環境,最好的的辦法還是停掉Tomcat,然後清除work下面的目錄檔案并且重新部署應用。

Mark Thomas解析應用程式或者庫程式在如下情況下會觸發記憶體洩露:

• JDBC驅動的注冊

• 一些日志架構

• 在ThreadLocals中儲存了對象但沒有删除它們

• 啟動了線程但沒停止

而 Java API 存在記憶體洩漏的地方包括:

1.使用 javax.imageio API ( Google Web Toolkit會用到)

2.使用 java.beans.Introspector.flushCaches()

3.使用 XML 解析器

4.使用 RMI 遠端方法調用

5.從 Jar 檔案中讀取資源

4. 在war檔案外使用别名去存儲靜态内容

Web應用程式需要靜态資源檔案,比如象CSS,Javascript和視訊檔案、圖檔檔案等。通常都把它們打包放在war檔案中,這将增加了WAR檔案的大小并且導緻很多重複的加載靜态資源。一個比較好的解決方法是使用Apache HTTP伺服器去管理這些靜态檔案資源,下面是一個apache httpd.conf檔案的配置摘錄:

  1. < Directory "/home/avneet/temp/static" >   
  2. Order allow,deny   
  3. Allow from all   
  4. < /Directory >   
  5. Alias /static "/home/avneet/temp/static"  

以上的設定,使得通路http://localhost/static時,能通路到放在/home/avneet/temp/static下的資源。

允許使用新的aliases屬性,指出靜态檔案資源的位置,可以通過使用Classloader.getResourceAsStream('/static/...')或者在連結中嵌入的方法讓Tomcat去解析絕對路徑,下面是一個在context.xml中配置的例子:

  1. < ?xml version="1.0" encoding="UTF-8"? >   
  2. < Context path="/Tomcat7demo" aliases="/static=/home/avneet/temp/static" >   
  3. < /Context >  

假設/home/avneet/temp/static這個檔案夾存放有一張圖檔bg.png,如果war檔案以Tomcat7demo的名字部署,那麼可以通過以下三個方式去通路這張圖檔

1.直接通路 http://localhost:8080/Tomcat7demo/static/bg.png

2.在HTML連結中通路:< img src="/Tomcat7demo/static/bg.png" / >

3.通過JAVA代碼通路: ByteArrayInputStream bais = (ByteArrayInputStream)getServletContext().getResourceAsStream("/static/bg.png");

使用aliases的好處是可以代替Apache的httpd.conf的設定,并且可以在servlet容器範圍内通路,并且不需要Apache。

Tomcat 7的增強特性

5. 對Servlet 3.0,JSP 2.2和JSP-EL 2。2的支援

Servlet 3的增強特性有:

• 可以在POJO或者過濾器filters中使用annotations注釋(在web.xml中不再需要再進行設定了)

• 可以将web.xml分塊進行管理了。也就是說,使用者可以編寫多個xml檔案,而最終在web.xml中組裝它們,這将大大降低web.xml的複雜性增強可讀性。比如, struts.jar和spring-mvc.jar每一個都可以有一個web-fragment.xml。開發者不再需要在web.xml中去配置它們了,在web-fragment.xml中的jar檔案會自動加載,并且struts/spring-mvc servlets和filters也會自動裝配設定。

• 異步處理web的請求----這個特性在Tomcat 6 中已經有了,現在在Tomcat 7中以Servlet 3标準規範化了,能讓使用異步I/O的web應用程式可以移植到不同的web容器中。異步處理使用非阻塞I/O,每次的HTTP連接配接都不需要對應一個線程。更少的線程可以為更多的連接配接提供服務。這對于需要長時間計算處理才能傳回結果的情景來說是很有用的,比如産生報表,Web Servce調用等。

• 安全的增強---Servlet 3.0現在使用SSL 去加強了會話session的跟蹤,代替了原來的cookie和URL重寫。

6. 更容易将Tomcat内嵌到應用去中去

Tomcat 7現在可以嵌入到應用程式中去,并可以通過程式去動态設定和啟動。象在CATALINA_HOME/conf/server.xml中的很多配置,現在都可以用程式動态去設定了。在Tomcat 7前,Tomcat 6提供了一個嵌入類,它能友善地去配置Tomcat。但在Tomcat 7中,這個類已被廢棄了。這個新的Tomcat 7的類,使用了幾個預設的配置元素,并提供了一個更容易和簡單的方法去嵌入Tomcat。

下面是CATALINA_HOME/conf/server.xml中的一些相關屬性和配置: 

  1. < Server >   
  2. < Service >   
  3. < Connector port="8080 >   
  4. < Engine >   
  5. < Host appBase="/home/avneet/work/Tomcat7demo/dist" / >   
  6. < /Engine >   
  7. < /Connector >   
  8. < /Service >   
  9. < /Server > 

我們可以通過程式去進行動态設定了:

  1. final String CATALINA_HOME = "/home/avneet/work/temp/Tomcat7demo/";   
  2. Tomcat Tomcat = new Tomcat();   
  3. Tomcat.setBaseDir( CATALINA_HOME );   
  4. Tomcat.setPort( 8080 );   
  5. Tomcat.addWebapp("/Tomcat7demo", CATALINA_HOME + "/webapps/Tomcat7demo.war");   
  6. Tomcat.start();   
  7. System.out.println("Started Tomcat");   
  8. Tomcat.getServer().await(); //Keeps Tomcat running until it is shut down   
  9. //Webapp Tomcat7demo accessible at http://localhost:8080/Tomcat7demo/  

7. 異步日志記錄

Tomcat 7現在包括了一個異步日志記錄器(AsyncFileHandler)。AsyncFileHandler繼承了FileHandler類并能代替FileHandler。使用AsyncFileHandler,時,隻需要在CATALINA_HOME/conf/logging.properties中把FileHandler全部替換為AsyncFileHandler就可以了。要注意的是異步日志不能跟log4一起工作。

當有日志發向AsyncFileHandler時,日志被加入到隊列中(java.util.concurrent.LinkedBlockingDeque)并且方法調用的資訊會馬上傳回不需要等待I/O寫到磁盤中。當類加載器加載AsyncFileHandler時,會有一個單獨的線程啟動,這個線程會從隊列中讀取日志資訊并且寫到磁盤中去。

這種方法的好處是如果I/O速度很慢(比如日志要儲存在遠端的裝置上)時,記錄日志的請求和處理過程不會顯得很慢。

AsyncFileHandler使用生産者和消費者的關系原理,在隊列中存儲日志資訊。隊列預設大小為10000。為了預防隊列溢出,預設是丢棄最後的資訊。預設的隊列大小和溢出的設定都可以通過啟動參數進行設定。

關于Tomcat 7的示例程式

Tomcat 7的自帶程式例子有兩個servlets,一個是示範了如何采用随機數的辦法防止CSRF攻擊,另外一個是描述了使用aliases。更新一下web/META-INF/context.xml,指出圖檔的絕對路徑即可順利運作。

通過ant運作build.xml去将它們部署到Tomcat 7中,使用如下兩個位址通路:

• http://localhost:8080/Tomcat7demo/csrf/

• http://localhost:8080/Tomcat7demo/alias/