權限控制是每一個系統都應該有的一個功能,有些只需要簡單控制一下就可以了,然而有些卻需要進行更加深入和細致的權限控制,尤其是對於一些MIS類系統,基於方法的權限控制就更加重要了。
用反射和自定義注解來實現基於struts2的方法級別的權限控制的主要思想是這樣的。
1、先定義一個用於識別在進行action調用的時候標注該方法調用是否需要權限控制,需要什么樣的權限的注解類。該注解類一般會包括兩個屬性,一個是需要的權限,一個是對應的action模塊。
2、然后就是在需要進行權限控制的action方法上加上該注解類,並標明其應該擁有的權限和對應的action。這樣一來在進行action調用的時候可以實現一個自己定義的interceptor來攔截所有的請求,這樣在攔截到請求的時候就可以通過ActionInvocation獲取到對應的action類的類文件和對應請求的方法名稱,然后利用反射來取得action類文件里面對應的請求方法Method,這樣就可通過該Method來判斷其是否擁有對應的權限控制注解,即看其是否需要進行權限控制,如果需要進行權限控制,就取得該注解並取得其對應的action名稱和需要的權限,然后通過session取得當前的用戶,並判斷當前用戶是否擁有對應的某種權限,如果其擁有該權限則繼續往下執行,否則,轉到自己處理無權限的機制上去。
下面是一段示例代碼:
1、定義一個注解,權限配置
package com.ljq.action; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定義一個注解,權限配置 * * @author jiqinlin * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Authority { /** 模塊 */ String module(); /** 權限值 */ String privilege(); }
2、用於攔截請求判斷是否擁有權限的攔截器AuthorityInterceptor
package com.ljq.action; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.log4j.Logger; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /** * 用於攔截請求判斷是否擁有權限的攔截器 * * @author jiqinlin * */ @SuppressWarnings("serial") public class AuthorityInterceptor extends AbstractInterceptor { private static final Logger logger=Logger.getLogger(AuthorityInterceptor.class); public String intercept(ActionInvocation invocation) throws Exception { String methodName = invocation.getProxy().getMethod(); Class clazz=invocation.getAction().getClass(); //獲取類對象 Method currentMethod = clazz.getMethod(methodName); //檢查Action類AnnotationTest是否含有@Authority注解 if (currentMethod.isAnnotationPresent(Authority.class)) { // 從session里取得當前的用戶 String currentUser = (String) ServletActionContext.getRequest() .getSession().getAttribute("currentUser"); // 取得權限驗證的注解 Authority authority = currentMethod.getAnnotation(Authority.class); // 取得當前請求的注解的action String module = authority.module(); // 取得當前請求需要的權限 String privilege = authority.privilege(); /** * 然后可以在此判斷當前用戶是否擁有對應的權限,如果沒有可以跳到指定的無權限提示頁面, * 如果擁有則可以 繼續往下執行。 * if (擁有對應的權限) { return invocation.invoke(); } * else { return "無權限"; } */ logger.info("++++++++++++++++++++++++++++++++++++++++++++++++++++++"); logger.info("用戶["+currentUser+"]在"+new SimpleDateFormat("yyyy-MM-dd hh24:mm:ss").format(new Date()) +"調用了["+clazz.getName()+"]類的["+methodName+"]方法,所在模塊["+module+"],擁有權限["+privilege+"]。"); logger.info("++++++++++++++++++++++++++++++++++++++++++++++++++++++"); } return invocation.invoke(); } }
3、需要進行權限控制的Action
package com.ljq.action; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Namespace; import org.apache.struts2.convention.annotation.Result; import org.apache.struts2.convention.annotation.Results; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") @Namespace(value="/") @Results(value = { @Result(name ="success", location = "/index.jsp"), @Result(name ="add", location = "/index.jsp"), }) @Action(value="action") public class AnnotationAction extends ActionSupport{ //訪問路徑:http://localhost:8083/struts2_annotation_auth/action!list.htm @Authority(module="annotation", privilege="list") public String list() throws Exception { System.out.println("-------execute---------"); return SUCCESS; } //訪問路徑:http://localhost:8083/struts2_annotation_auth/action!add.htm @Authority(module = "annotation", privilege = "add") public String add() { System.out.println("-------test---------"); return "add"; } }
4、在struts2的配置文件里面配置自定義的權限控制攔截器
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!--個人發現2.3.4.1注解該項必須配置,否則會有問題,如攔截器無效等。--> <constant name="struts.convention.default.parent.package" value="default-package" /> <!-- 可選配置項 --> <!-- 是否支持方法動態調用,即action名!方法名,如user!addUser.action調用user.action中的addUser方法. 不建議使用,這種方法安全性較差 <constant name="struts.enable.DynamicMethodInvocation" value="false" />--> <!-- 開發模式下使用,這樣可以打印出更詳細的錯誤信息 --> <constant name="struts.devMode" value="true" /> <!-- 指定Web應用的默認編碼集,相當於調用HttpServletRequest的setCharacterEncoding方法 --> <constant name="struts.i18n.encoding" value="UTF-8" /> <!-- 該屬性指定需要Struts 2處理的請求后綴,該屬性的默認值是action,即所有匹配*.action的請求都由Struts2處理。如果用戶需要指定多個請求后綴,則多個后綴之間以英文逗號(,)隔開。 --> <constant name="struts.action.extension" value="htm" /> <!-- 設置瀏覽器是否緩存靜態內容,默認值為true(生產環境下使用),開發階段最好關閉 --> <constant name="struts.serve.static.browserCache" value="false" /> <!-- 當struts的配置文件修改后,系統是否自動重新加載該文件,默認值為false(生產環境下使用),開發階段最好打開 --> <constant name="struts.configuration.xml.reload" value="true" /> <constant name="struts.ognl.allowStaticMethodAccess" value="true"/> <!-- 配置log4j的日志級別 --> <!-- <constant name="log4j.logger.org.apache.struts2.convention" value="DEBUG" /> --> <package name="default-package" extends="convention-default" abstract="true"> <!-- 定義攔截器 --> <interceptors> <!-- 申明自定義的權限控制攔截器 --> <interceptor name="authorityInterceptor" class="com.ljq.action.AuthorityInterceptor" /> <!-- 把自定義的權限控制攔截器和默認的攔截器棧加到新的自定義的攔截器棧 --> <interceptor-stack name="myInterceptors"> <interceptor-ref name="timer" /> <interceptor-ref name="authorityInterceptor" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <!-- 指定新的自定義的攔截器棧為默認的攔截器棧,這樣自定義的權限控制攔截器就可以發揮作用了 --> <default-interceptor-ref name="myInterceptors"></default-interceptor-ref> </package> </struts>
訪問http://localhost:8083/struts2_annotation_auth/action!list.htm,執行結果如下:
[INFO ] [2012-11-29 15:33:26 681]-com.ljq.action.AuthorityInterceptor.44[http-8083-1] - ++++++++++++++++++++++++++++++++++++++++++++++++++++++ [INFO ] [2012-11-29 15:33:26 682]-com.ljq.action.AuthorityInterceptor.45[http-8083-1] - 用戶[null]在2012-11-29 0324:33:26調用了[com.ljq.action.AnnotationAction]類的[list]方法,所在模塊[annotation],擁有權限[list]。 [INFO ] [2012-11-29 15:33:26 682]-com.ljq.action.AuthorityInterceptor.47[http-8083-1] - ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -------execute--------- [INFO ] [2012-11-29 15:33:27 441]-com.opensymphony.xwork2.interceptor.TimerInterceptor.42[http-8083-1] - Executed action [//action!list] took 765 ms.
訪問http://localhost:8083/struts2_annotation_auth/action!add.htm,執行結果如下:
[INFO ] [2012-11-29 15:33:53 604]-com.ljq.action.AuthorityInterceptor.44[http-8083-1] - ++++++++++++++++++++++++++++++++++++++++++++++++++++++ [INFO ] [2012-11-29 15:33:53 605]-com.ljq.action.AuthorityInterceptor.45[http-8083-1] - 用戶[null]在2012-11-29 0324:33:53調用了[com.ljq.action.AnnotationAction]類的[add]方法,所在模塊[annotation],擁有權限[add]。 [INFO ] [2012-11-29 15:33:53 605]-com.ljq.action.AuthorityInterceptor.47[http-8083-1] - ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -------test--------- [INFO ] [2012-11-29 15:33:53 624]-com.opensymphony.xwork2.interceptor.TimerInterceptor.42[http-8083-1] - Executed action [//action!add] took 20 ms.