關於struts2中的攔截器和登陸驗證
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
今天給幾個熱愛JAVA的同學們講了Struts2的一些知識,重點討論了其最具價值的攔截器。
不知道大家還記不記得,在《Struts2之伺服器端驗證》裡我說過這樣一句話“在到達Login Action之前,驗證已經完成了”。我很希望有人能提出這是為什麼,這樣我就可以說,這是攔截器的功勞,我們就可以研究攔截器了。
其實,攔截器並不難,也不是十分難懂的東西。在這裡再做一些補充.
開啟幫助文件(struts-2.0.6\\docs\\docs\\interceptors.html)的攔截器部分。
能讀懂英文技術文件是程式設計師必備的基本素質之一,慢慢來吧,只要靜下心來逐字逐句的推敲,沒有什麼理解不了的。實在看不懂,那就看看[Action Lifecyle]這張圖吧。Action生命週期,Action被一些攔截器包圍著,也就是說在Action執行之前或之後,攔截器會被執行。
這就是攔截器,把程式看作是一個順序執行的流,在執行Action的程式碼之前或之後,攔截器會打斷Action的執行,讓程式先執行攔截器的程式碼,這也許就是為什麼把它叫做攔截器的原因。有的時候,攔截器還會阻止Action的執行,比如說在驗證失敗的情況下,應該讓程式返回到輸入介面讓使用者重新輸入。
從圖上還可以看出,一個Action之外不是隻有一個攔截器,那麼先執行哪個攔截器呢?Struts2把多個攔截器放在了一個棧中,我們把它叫做攔截器棧。一方面,棧可以解決攔截器的執行順序問題,另一方面,把相關的攔截器放在一個棧中,管理起來也比較方便。
Struts2的一個優點是它的可配置性,我們可以根據實際情況選擇需要的功能。當然也給我們帶來了一些麻煩,就拿攔截器來說吧,要想讓攔截器起作用,先要對它進行配置。配置攔截器,需要在struts.xml中加入相關配置:
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="timer" class=".."/>
<interceptor name="logger" class=".."/>
</interceptors>
<action name="login"
class="tutorial.Login">
<interceptor-ref name="timer"/>
<interceptor-ref name="logger"/>
<result name="input">login.jsp</result>
<result name="success"
type="redirect-action">/secure/home</result>
</action>
</package>
=====================================================================
<interceptors>和</interceptors>標記對錶示要配置一些攔截器,裡面的每一條是一個攔截器。
<interceptor name="timer" class=".."/>
interceptor表示這是一個攔截器,name屬性是給它起一個名字,class屬性是指出它的實現類,也是程式碼的實際位置。
攔截器是攔截Action的,當然也要在Action的配置里加入對它的引用,指出這個Action要使用哪個攔截器。
<interceptor-ref name="timer"/>這句話是告訴Struts框架,login Action需要使用前面的timer攔截器。
有人也許會說,我們之前並沒有配置攔截器,但剛才好像說過,不是也使用到攔截器了麼?的確,可能是Struts2的開發者怕配置起來太麻煩了,沒有人用吧:P,所以給我預先配置好了一些預設的攔截器和攔截器棧。
在struts-default.xml(struts-default.xml在struts2-core-2.0.6.jar包中)裡面定義了很多攔截器:
<interceptor name="alias" class="com.opensymphony.xwork.interceptor.AliasInterceptor"/>
......
裡面的validation和i18n就是我們之前用的驗證和國際化功能。
還有很多攔截器棧:
<!-- Basic stack -->
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="static-params"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
...
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="model-driven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="static-params"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>
</interceptor-stack>
<default-interceptor-ref name="defaultStack"/>
最後這條比較主要,是框架預設使用的攔截器棧,也就是說,defaultStack棧中的攔截器會預設起作用。
這也是我們沒有配置validation和i18n,它們就起作用的原因。
順便說一下,攔截器應該屬於AOP(面向方面程式設計)思想的一種實現,它的確給開發帶來了很多好處,一方面可以把一個大的問題分解成多個小問題分別處理,另一方面可以使Action專注與處理自己的事情,把相關的功能分散給各個攔截器處理。
在《Struts2之伺服器端驗證》中我們詳細討論了關於登陸驗證的問題,只有通過登陸驗證,拿到令牌的使用者才能進入我們的系統。為此我們建立了登陸驗證頁面和相應的Action類,登陸驗證(Login.jsp)負責提供給使用者輸入帳號和密碼的機會,Login Action負責驗證使用者輸入的帳號和密碼是否正確,驗證通過的情況下把帳號和密碼儲存到Session中,就相當於使用者拿到了一塊令牌,就是一個合法使用者了。
我們的目的是限制非法使用者,也就是沒有拿到令牌的使用者,不能訪問我們的系統。但是我們只進行了登陸驗證和儲存令牌的工作,並沒有做限制非法使用者的工作。現在不守規矩的使用者就可以直接在瀏覽器的位址列裡輸入:http://localhost:8080/Success.jsp
訪問我們的Success.jsp頁面,這當然是不能容忍的。
在沒有學習攔截器之前,我們當然可以在Success.jsp頁面里加程式碼進行限制,先從Session中取出使用者名稱和密碼,如果取到了則說明使用者已經登陸系統了,否則讓瀏覽器顯示Login.jsp讓使用者登陸系統,這樣做一點問題也沒有,就相當於我們在每一個房間門口都安排了一個檢查令牌的人一樣,萬無一失。不過,如果系統的受限制資源多起來的時候,比如說像Success.jsp或Action很多,幾十個,幾百個的時候,這樣的程式碼我們就要寫很多遍,這當然也是聰明的我們所不能容忍的:P
顯然,讓攔截器來做這項工作再合適不過了。我們可以新增一個攔截器,讓它在這些受限制的資源被執行之前先執行,在攔截器裡我們檢查使用者是否登陸了系統,如果登陸了才讓他們訪問,否則送給使用者一個Login.jsp讓使用者登陸。這樣我們的檢查程式碼只需要寫一次就一勞永逸了。
明白了原理之後還需要做幾項具體的工作:
1,自己寫一個攔截器,實現檢查使用者是否登陸的功能。
2,新增攔截器的配置,讓我們自己寫的攔截器起作用。
首先我們來完成第一個任務,開啟:
struts-2.0.6docsdocsguides.html裡面的
struts-2.0.6docsdocswriting-interceptors.html幫助。
從這個幫助裡我們可以看出,攔截器必須實現com.opensymphony.xwork2.interceptor.Interceptor介面。根據經驗,init()方法應該是初始化攔截器的方法,可以把一些初始化工作的程式碼放在它裡面,destroy()方法與之相反,在攔截器被銷燬之前,讓我們有機會執行一些善後工作。
顯然intercept()方法,是新增真正執行攔截工作的程式碼的地方,如果我們不需要初始化和清理的操作,可以直接繼承com.opensymphony.xwork2.interceptor.AbstractInterceptor類,覆蓋它的intercept()方法。這個方法有個ActionInvocation型別的引數,可以用它取得Session或轉發請求等操作。
先在src下建立一個包:tutorial.interceptor
在這個包下建立一個類LogonInterceptor繼承於AbstractInterceptor,覆蓋intercept()方法:
package tutorial.interceptor;
import java.util.Map;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class LogonInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
// 取得請求的Action名
String name = invocation.getInvocationContext().getName();
if (name.equals("Login")) {
// 如果使用者想登入,則使之通過
return invocation.invoke();
} else {
// 取得Session。
ActionContext ac = invocation.getInvocationContext();
Map session = (Map)ac.get(ServletActionContext.SESSION);
if (session == null) {
// 如果Session為空,則讓使用者登陸。
return "login";
} else {
String username = (String)session.get("username");
if (username == null) {
// Session不為空,但Session中沒有使用者資訊,
// 則讓使用者登陸
return "login";
} else {
// 使用者已經登陸,放行~
return invocation.invoke();
}
}
}
}
}
今天的內容有點多,接下來還要對這個攔截器進行配置:
關於怎樣配置一個攔截器使之對所有的Action起作用請參考:
struts-2.0.6docsdocshow-do-we-configure-an-interceptor-to-be-used-with-every-action.html
修改struts.xml,
1,自定義攔截器
2,重定義預設攔截器堆疊
3,新增一個global-results,使用者在驗證失敗的情況下跳轉到登陸驗證頁面
struts.xml的完整內容:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts><!-- Configuration for the default package. -->
<constant name="struts.custom.i18n.resources" value="globalMessages" />
<include file="struts-validation.xml" />
<package name="default" extends="struts-default">
<!-- 自定義攔截器 -->
<interceptors>
<interceptor name="logon" class="tutorial.interceptor.LogonInterceptor"/>
<!-- 自定義攔截器堆疊 -->
<interceptor-stack name="myStack">
<interceptor-ref name="logon"/>
<!-- 引用預設的攔截器堆疊 -->
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 重定義預設攔截器堆疊 -->
<default-interceptor-ref name="myStack"/>
<global-results>
<result name="login" type="redirect-action">Login!input.action</result>
</global-results>
<action name="HelloWorld" class="tutorial.HelloWorld">
<result>/HelloWorld.jsp</result>
</action>
<action name="Login" class="tutorial.Login">
<result>/Success.jsp</result>
<result name="input">/Login.jsp</result>
</action>
</package>
</struts>
儲存修改並佈署專案到Tomcat下,啟動Tomcat
在瀏覽器的位址列中輸入:
http://localhost:8080/tutorial/HelloWorld.action
系統會自動轉到Login頁面。