【Struts2】:Interceptor實戰之許可權控制

連江偉發表於2016-05-31
        之前的部落格介紹了攔截器的概念以及Struts2自帶以及自定義攔截器的一些基礎知識,但是攔截器究竟如何應用在實際專案裡,或許你還很迷惑,那麼本次部落格我們就一起來實戰一下,也算是對攔截器的一個總結和應用實踐。
        我們在做任何的資訊管理系統的時候,無可避免的要進行許可權控制,對於登入使用者的身份以及所擁有的許可權進行驗證,不讓不合法的使用者隨意更改我們的資料和程式,以保證系統的安全性。這樣一個非常普遍的功能,我們要使用Struts2的攔截器去實現許可權檢查,當瀏覽者需要請求執行某個操作時,應用系統需要先檢查使用者是否登入,以及是否有足夠的許可權來執行該操作。
        在這個Demo中,我們要求使用者必須登入,並且必須為指定使用者名稱的使用者才可以檢視系統中的某個檢視資源,否則,系統直接轉入登入頁面。對於這樣的需求,可以在每個Action執行實際的處理邏輯之前,先進行許可權驗證邏輯,但是這種做法不利於程式碼的複用,因為大部分Action裡的許可權驗證程式碼都大同小異,故將這些許可權驗證的程式碼放在攔截器中將顯得更加方便和靈活,也更為專業。
        檢查使用者是否登入,通常都是通過跟蹤使用者的Session來完成的,通過ActionContext即可訪問到session中的屬性,攔截器的intercept方法的invocation引數可以很容易地訪問到請求相關的ActionContext例項。

        我們先來編寫許可權驗證攔截器類的程式碼,具體程式碼如下

// 許可權檢查攔截器繼承AbstractInterceptor類
public class AuthorityInterceptor
	extends AbstractInterceptor
{
	// 攔截Action處理的攔截方法
	public String intercept(ActionInvocation invocation)
		throws Exception
	{
		// 取得請求相關的ActionContext例項
		ActionContext ctx = invocation.getInvocationContext();
		Map session = ctx.getSession();
		// 取出Session裡的user屬性
		String user = (String)session.get("user");
		//如果沒有登入,或者登入所用的使用者名稱不是admin,都返回重新登入
		if (user != null && user.equals("admin") )
		{
			return invocation.invoke();
		}
		// 如果沒有登入,將伺服器提示放入ActionContext中
		ctx.put("tip" ,"您還沒有登入,請輸入管理員賬號登入系統");
		// 返回login的邏輯檢視
		return Action.LOGIN;
	}
}

        從上面的程式碼中可以看到,先通過ActionInvocation引數獲取使用者的Session例項的引用,然後從中取出user屬性,通過判斷該屬性值來確定使用者是否登入系統,從而判斷是否需要轉入登入頁面。
        實現了上述的許可權驗證攔截器,就可以在配置檔案中隨意使用該攔截器對需要實現許可權控制的Action進行配置,使之具有許可權控制的功能。具體在struts.xml檔案中如何定義和應用於Action中,請看如下具體配置:
<package name="lee" extends="struts-default">
		<!-- 使用者攔截器定義在該元素下 -->
		<interceptors>
			<!-- 定義了一個名為authority的攔截器 -->
			<interceptor name="authority" 
				class="org.ljw.app.interceptor.AuthorityInterceptor"/>
		</interceptors>

		<!-- 定義全域性Result -->
		<global-results>
			<!-- 當返回login檢視名時,轉入loginForm.jsp頁面 -->
			<result name="login">/WEB-INF/content/loginForm.jsp</result>
		</global-results>

		<action name="login" class="org.ljw.app.action.LoginAction">
			<result name="error">/WEB-INF/content//error.jsp</result>
			<result>/WEB-INF/content/welcome.jsp</result>
		</action>
		<!-- 定義一個名為viewBook的Action,其實現類為ActionSupport -->
		<action name="viewBook">
			<!-- 返回success檢視名時,轉入viewBook.jsp頁面 -->
			<result>/WEB-INF/content/viewBook.jsp</result>
			<interceptor-ref name="defaultStack"/>
			<!-- 應用自定義攔截器 -->
			<interceptor-ref name="authority"/>
		</action>
		<action name="*">
			<result>/WEB-INF/content/{1}.jsp</result>
		</action>
	</package>
上面名為viewBook的Action,沒有指定class屬性,預設使用ActionSupport類,配置該Action時,只指定了一個結果對映,指定系統返回success字串時,系統將轉入viewBook頁面,但並未配置login檢視名對應的jsp頁面。
        考慮到該攔截器的複用性問題,系統可能每個頁面都需要進行許可權控制,因此可以將login的結果對映定義為全域性結果對映,具體配置方法看上面程式碼。如果要簡化struts.xml檔案的配置量,避免在每個Action中重複配置該攔截器,可以將該攔截器與Struts2預設的攔截器棧放在一起,定義成新的預設攔截器棧mydefaultstack,這樣就不用在每個Action中重複定義許可權驗證攔截器了,預設已有,這體現了抽象—>封裝的思想,跟我們編寫程式碼時遇到重複程式碼就抽出來進行封裝複用是一個道理。

相關文章