Struts 2

Struts 2

Struts2是一個基於MVC設計模式的Web應用框架,它本質上相當於一個servlet,在MVC設計模式中,Struts2作為控制器(Controller)來建立模型與視圖的數據交互。Struts 2是Struts的下一代產品,是在 struts 1和WebWork的技術基礎上進行了合併的全新的Struts 2框架。其全新的Struts 2的體系結構與Struts 1的體系結構差別巨大。Struts 2以WebWork為核心,採用攔截器的機制來處理用戶的請求,這樣的設計也使得業務邏輯控制器能夠與ServletAPI完全脫離開,所以Struts 2可以理解為WebWork的更新產品。雖然從Struts 1到Struts 2有著太大的變化,但是相對於WebWork,Struts 2的變化很小。

發展歷史


2000年5月發展至今,Struts1已經成為了一個高度成熟的框架,不管是穩定性還是可靠性都得到了廣泛的證明。市場佔有率超過20%,擁有豐富的開發人群,幾乎已經成為了事實上的工業標準。但是隨著時間的流逝,技術的進步,Struts1的局限性也越來越多地暴露出來,並且制約了Struts1的繼續發展。
對於Struts1框架而言,由於與JSP/Servlet耦合非常緊密,因而導致了一些嚴重的問題。首先,Struts1支持的表現層技術單一。由於Struts1出現的年代比較早,那個時候沒有FreeMarker、Velocity等技術,因此它不可能與這些視圖層的模版技術進行整合。其次,Struts1與Servlet API的嚴重耦合,使應用難於測試。最後,Struts1代碼嚴重依賴於Struts1 API,屬於侵入性框架。
從目前的技術層面上看,出現了許多與Struts1競爭的視圖層框架,比如JSF、Tapestry和spring MVC等。這些框架由於出現的年代比較近,應用了最新的設計理念,同時也從Struts1中吸取了經驗,克服了很多不足。這些框架的出現也促進了Struts的發展。
Struts1已經分化成了兩個框架:第一個是在傳統的Struts1的基礎上,融合了另外的一個優秀的Web框架WebWork的Struts2。Struts 2雖然是在Struts1的基礎上發展起來的,但是實質上是以WebWork為核心的。Struts2為傳統的Struts1注入了WebWork的先進的設計理念,統一了Struts1和WebWork兩個框架。
Struts1分化出來的另外一個框架是Shale。這個框架遠遠超出了Struts1原有的設計思想,與原有的Struts1關聯很少,使用了全新的設計思想。Shale更像一個新的框架而不是Struts1的升級。

工作原理


Struts 2的工作原理圖:
Struts 2
Struts 2
Struts 2
Struts 2

體系結構


應用流程註解
Struts 2
Struts 2
當Web容器收到請求(HttpServletReques t)它將請求傳遞給一個標準的的過濾鏈包括(ActionContextCleanUp)過濾器。
經過Other filters(SiteMesh ,etc),需要調用FilterDispatcher核心控制器,然後它調用ActionMapper確定請求哪個Action,ActionMapper返回一個收集Action詳細信息的ActionMaping對象。
FilterDispatcher將控制權委派給ActionProxy,ActionProxy調用配置管理器(ConfigurationManager) 從配置文件中讀取配置信息(struts.xml),然後創建ActionInvocation對象。
ActionInvocation在調用Action之前會依次的調用所用配置攔截器(Interceptor N)一旦執行結果返回結果字元串ActionInvocation負責查找結果字元串對應的(Result)然後執行這個Result Result會調用一些模版(JSP)來呈現頁面。
攔截器(Interceptor N)會再被執行(順序和Action執行之前相反)最後響應(HttpServletResponse)被返回在web.xml中配置的那些過濾器和核心控制器(FilterDispatcher)。

技術改進


Struts 2對Struts1進行了巨大的改進。主要表現在如下幾個方面:
在Action的實現方面
Struts1要求必須統一擴展自Action類,而Struts2中可以是一個POJO
線程模型方面
Struts1的Action是單實例的,一個Action的實例處理所有的請求。Struts 2的Action是一個請求對應一個實例(每次請求時都新new出一個對象),沒有線程安全方面的問題。
Servlet依賴方面
Struts1的Action依賴於Servlet API,比如Action的execute方法的參數就包括request和response對象。這使程序難於測試。Struts2中的Action不再依賴於Servlet API,有利於測試,並且實現TDD。
封裝請求參數
Struts1中強制使用ActionForm對象封裝請求的參數。Struts2可以選擇使用POJO類來封裝請求的參數,或者直接使用Action的屬性。
表達式語言方面
Struts1中整合了EL,但是EL對集合和索引的支持不強,Struts2整合了OGNL(Object Graph NavigationLanguage)。
綁定值到視圖技術
Struts1使用標準的JSP,Struts2使用“ValueStack”技術。
類型轉換
Struts1中的ActionForm基本使用String類型的屬性。Struts2中使用OGNL進行轉換,可以更方便的使用。
Struts1中支持覆蓋validate方法或者使用Validator框架。Struts2支持重寫validate方法或者使用XWork的驗證框架。
Action執行控制的對比
Struts1支持每一個模塊對應一個請求處理,但是模塊中的所有Action必須共享相同的生命周期。Struts2支持通過攔截器堆棧為每一個Action創建不同的生命周期。
攔截器的應用
攔截器,在AOP(Aspect-Oriented Programming)中用於在某個方法或欄位被訪問之前,進行攔截然後在之前或之後加入某些操作。攔截是AOP的一種實現策略。
在Webwork的中文文檔的解釋為——攔截器是動態攔截Action調用的對象。它提供了一種機制可以使開發者可以定義在一個action執行的前後執行的代碼,也可以在一個action執行前阻止其執行。同時也是提供了一種可以提取action中可重用的部分的方式。
談到攔截器,還有一個詞大家應該知道——攔截器鏈(Interceptor Chain,在Struts 2中稱為攔截器棧Interceptor Stack)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或欄位時,攔截器鏈中的攔截器就會按其之前定義的順序被調用。
1.攔截器的實現原理:
大部分時候,攔截器方法都是通過代理的方式來調用的。Struts 2的攔截器實現相對簡單。當請求到達Struts 2的ServletDispatcher時,Struts 2會查找配置文件,並根據其配置實例化相對的攔截器對象,然後串成一個列表(list),最後一個一個地調用列表中的攔截器。
2.攔截器的配置
Struts 2已經為您提供豐富多樣的,功能齊全的攔截器實現。大家可以至struts2的jar包內的struts-default.xml查看關於默認的攔截器與攔截器鏈的配置。
在struts.xml文件中定義攔截器,攔截器棧:
1
2
3
4
5
6
7
8
9
10
11
12
......

對比


Struts和Webwork同為服務於Web的一種MVC框架,從某種程度上看,Struts2是從WebWork2上升級得到的。甚至Apache的官方文檔也講:WebWork2到Struts2是平滑的過渡。我們甚至也可以說Struts2就是WebWork2.3而已。在很多方面Struts僅僅是改變了WebWork下的名稱。Struts2對應的有自己的標籤,並且功能強大。Webwork也有自己的標籤。在2005年12月,WebWork與Struts Ti決定合併,在此同時, Struts Ti 改名為 Struts Action Framework 2.0,成為Struts真正的下一代。

基類


標準返回值

ActionSupport基類中定義了五個標準的返回值,當然我們可以自己隨意定義返回的名字
String SUCCESS = "success"; //默認是 SUCCESS 類型
String NONE = "none";
String ERROR = "error";
String INPUT = "input";
String LOGIN = "login";

方法

ActionSupport基類定義了了一些方法,程序員自己寫的action如果繼承了ActionSupport基類,就可以應用這些方法,很方便解決一些問題。
一些比較常用的方法:
getText(String aTextName);//國際化用到
...//getText(String aTextName)的重載方法
addActionMessage(String aMessage);
addFieldError(String fieldName, String errorMessage);
//校驗失敗后返回給客戶端的信息,struts2 標籤可以取得
addActionError(String anErrorMessage);

ResultType


在默認時,標籤的type屬性值是“dispatcher”(實際上就是轉發,forward)。開發人員可以根據自己的需要指定不同的類型,如redirect、stream等。如下面代碼所示:
/result.jsp
這此result-type可以在struts2-core-2.0.11.1.jar包或struts2源代碼中的struts-default.xml文件中找到,在這個文件中找到標籤,所有的result-type都在裡面定義了。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dispatcher
用來轉向頁面,通常處理JSP
1org.apache.struts2.dispatcher.ServletDispatcherResult
freemarker
處理FreeMarker模板
1org.apache.struts2.views.freemarker.FreemarkerResult
httpheader
控制特殊HTTP行為的結果類型
1org.apache.struts2.dispatcher.HttpHeaderResult
stream
向瀏覽器發送InputStream對象,通常用來處理文件下載,還可用於返回AJAX數據
1org.apache.struts2.dispatcher.StreamResult
StreamResult等價於在Servlet中直接輸出Stream流。這種Result被經常使用於輸出圖片、文檔等二進位流到 客戶端。通過使用StreamResult,我們只需要在Action中準備好需要輸出的InputStream即可。
配置:
1
2
3
4
5
6
image/jpeg
imageStream
filename="document.pdf"
1024
同時,StreamResult支持許多參數,對輸出的Stream流進行參數控制。
velocity
處理Velocity模板
1org.apache.struts2.dispatcher.VelocityResult
隨著模板技術的越來越流行,使用Freemarker或者Velocity模板進行View層展示的開發者越來越多。Struts2同樣為模板作為Result做出了支持。由於模板的顯示需要模板(Template)與數據(Model)的緊密配合,所以在Struts2中,這兩個Result的主要工作是為模板準備數據。
xslt
處理XML/XLST模板
1org.apache.struts2.views.xslt.XSLTResult
plainText
顯示原始文件內容,例如文件源代碼
1org.apache.struts2.dispatcher.PlainTextResult
chain
用來處理Action鏈
1com.opensymphony.xwork2.ActionChainResult
chain其實只是在一個action執行完畢之後,forward到另外一個action,所以他們之間是共享HttpServletRequest的。在使用chain作為Result時,往往會配合使用ChainingInterceptor。ChainingInterceptor的作用是在Action直接傳遞數據。事實上,源Action中ValueStack的數據會被做一次Copy,這樣,2個Action中的數據都在ValueStack中,使得對於前台來說,通過ValueStack來取數據,是透明而共享的。比如說,一張頁面中,你可能有許多數據要顯示,而某些數據的獲取方式可能被很多不同的頁面共享(典型來說,“推薦文章”這個小欄目的數據獲取,可能會被很多頁面所共享)。這種情況下,可以把這部分邏輯抽取到一個獨立Action中,並使用chain,將這個Action與主Action串聯起來。這樣,最後到達頁面的時候,頁面始終可以得到每個Action中的數據。
從實戰上講,使用chain作為Result也的確存在著上面所說的許多問題,我個人也是非常不推崇濫用這種Result。尤其是,對於使用Spring和Hibernate的朋友來說,如果你開啟OpenSessionInView模式,那麼Hibernate的session是跟隨HttpServletRequest的,所以session在整個action鏈中共享。這會為我們的編程帶來極大的麻煩。因為我們知道Hibernate的session會保留一份一級緩存,在action鏈中,共享一級緩存無疑會為你的調試工作帶來很大的不方便。
所以,謹慎使用chain作為你的Result,應該成為一條最佳實踐。
redirect
重定向到一個URL
1org.apache.struts2.dispatcher.ServletRedirectResult
如果你在Action執行完畢后,希望執行另一個Action,有2種方式可供選擇。一種是forward,另外一種是redirect。有關forward和redirect的區別,這裡我就不再展開,這應該屬於Java程序員的基本知識。在Struts2中,分別對應這兩種方式的Result,就是chain和redirect。
先來談談redirect,既然是重定向,那麼源地址與目標地址之間是2個不同的HttpServletRequest。所以目標地址將無法通過ValueStack等Struts2的特性來獲取源Action中的數據。
同時,Redirect的Result支持在配置文件中,讀取並解析源Action中ValueStack的值,並成為參數傳遞到Redirect的地址中。
redirectAction
重定向到一個Action
1org.apache.struts2.dispatcher.ServletActionRedirectResult

區別比較


redirect和redirectAction chain的區別
struts2中關於result的返回類型一般我們是轉發到一個jsp頁面或者是html頁面等,但是struts2中的result的返回類型還有redirect,redirectAction,chain。對於這三種返回類型之間肯定是有區別的,下面我們來看看關於redirect redirectAction chain這三種struts2的返回類型之間的區別。當使用type=“redirectAction”或type=“redirect”提交到一個action並且需要傳遞一個參數時。這裡是有區別的:使用type=“redirectAction”時,結果就只能寫Action的配置名,不能帶有後綴:“.action”
Xml代碼
1
2
3
User?u_id=${loginBean.u_id}
使用type=“redirect”時,結果應是action配置名+後綴名
Xml代碼
1
2
3
User.action?u_id=${loginBean.u_id}
redirect:action處理完后重定向到一個視圖資源(如:jsp頁面),請求參數全部丟失,action處理結果也全部丟失。
redirect-action:action處理完后重定向到一個action,請求參數全部丟失,action處理結果也全部丟失。
chain:action處理完後轉發到一個action,請求參數全部丟失,action處理結果不會丟失。

出現漏洞


曝出高危安全漏洞
Struts2曝出2個高危安全漏洞,一個是使用縮寫的導航參數前綴時的遠程代碼執行漏洞,另一個是使用縮寫的重定向參數前綴時的開放式重定向漏洞。這些漏洞可使黑客取得網站伺服器的“最高許可權”,從而使企業伺服器變成黑客手中的“肉雞”。
解決措施
Apache Struts團隊已發布了最新的Struts 2.3.15.1,修復了上述漏洞,建議採用Struts 2.0至Struts 2.3的網站開發者儘快升級至最新版。
帶來影響
據烏雲平台漏洞報告,淘寶、京東、騰訊等大型網際網路廠商均受此影響,而且漏洞利用代碼已經被強化,可直接通過瀏覽器的提交對伺服器進行任意操作並獲取敏感內容。Struts漏洞影響巨大,受影響站點以電商、銀行、門戶、政府居多,而且一些自動化、傻瓜化的利用工具開始出現,填入地址可直接執行伺服器命令,讀取數據甚至直接關機等操作。
瑞星安全專家介紹,本次曝出的2個漏洞是由於縮寫的導航和重定向前綴“action:”、 “redirect:”、 “redirectAction:”造成的。瑞星安全專家表示,由於這些參數前綴的內容沒有被正確過濾,導致黑客可以通過漏洞執行命令,獲取目標伺服器的信息,並進一步取得伺服器最高控制權。屆時,被攻擊網站的資料庫將面臨全面泄密的威脅,同時黑客還可以通過重定向漏洞的手段,對其他網民進行釣魚攻擊或掛馬攻擊。