利用基於@AspectJ的AOP實現權限控制


一. AOP與@AspectJ

       AOP 是 Aspect Oriented Programming 的縮寫,意思是面向方面的編程。我們在系統開發中可以提取出很多共性的東西作為一個 Aspect,可以理解為在系統中,我們需要很多次重復實現的功能。比如計算某個方法運行了多少毫秒,判斷用戶是不是具有訪問權限,用戶是否已登錄,數據的事務處理,日志記錄等等。

AOP的術語

  • 連接點(Joinpoint)

            程序執行的某個特殊位置:比如類開始初始化前,類初始化后,某個方法調用前,調用后等。 連接點 可 以 理解為AOP向目標類織入代碼的地方。

  • 切點(Pointcut)

            每一個類都有許多的連接點,所以AOP通過切點來定位特定的連接點。可以通過數據庫查詢的概念來理解切點和連接點的關系:連接點相當於數據庫中的記錄,而切點就是查詢條件。切點和連接點不是一對一的關系,一個切點可以匹配多個連接點。

  • 增強(Advice)

            增強就是織入到目標類連接點上的一段代碼。它既包含了添加到目標連接點上的一段執行邏輯,也包含了用於定位連接點的方位信息。

        AspectJ是語言級的AOP實現,它擴展了AOP的語法,能夠在編輯提供橫切代碼的織入。@AspectJ是AspecJ1.5新增的功能,它通過JDK5.0的注解技術,允許開發者在POJO中定義切面。

二. 自定義注解

       因為@AspectJ是基於JDK5.0的注解技術實現,所以我們有必要了解一下注解相關的知識,並學習如何自定義一個注解。

       注解(Annotation)是代碼的附屬信息,它可以用來修飾類,屬性,方法,同時它遵循一個基本原則,注解不能直接干擾程序代碼的運行,無論是增加或者刪除注解,代碼都能正常運行。

(1)一個簡單的注解類

     Java規定使用@interface修飾定義的注解類。一個注解類可以擁有多個成員,成員聲明和接口方法聲明類似。

   1: package com.wbl.aop;
   2:  
   3: import java.lang.annotation.ElementType;
   4: import java.lang.annotation.Retention;
   5: import java.lang.annotation.RetentionPolicy;
   6: import java.lang.annotation.Target;
   7:  
   8: /**
   9:  * Created by Lulala on 2015/7/15.
  10:  */
  11: @Retention(RetentionPolicy.RUNTIME)         //聲明注解的保留期限
  12: @Target(ElementType.METHOD)                 //聲明可以使用該注解的目標類型
  13: public @interface UserAccessAnnotation {    //定義注解
  14:     ACCESS value() default ACCESS.LOOK;     //聲明注解成員
  15: }

三. @AspectJ的使用

Spring采用AspectJ提供的@AspectJ注解類庫及相應的解析庫,所以需要在項目中導入aspectjweaver.jar類包。

(1)定義一個切面

   1: import org.aspectj.lang.annotation.Aspect;
   2: import org.aspectj.lang.annotation.Before;
   3: @Aspect         //通過注解將AspectTest標識為一個切面                    
   4: public class AspectTest{
   5:     @Before("execution(* set(..))")                //定義切點和增強類型
   6:     public void before(){                        //增強的橫切邏輯
   7:         System.out.println("Aspect before");
   8:     }
   9: }

       首先,在AspectTest類的定義處,標注了一個@Aspect的注解,表示這個類是一個切面,其次在before方法定義處,標注了@Before注解,並為該注解提供了成員值"execution(* set(..))"。@Before注解表示該增強是前置增強,成員值是一個切點表達式,表示在目標類的set方法上織入增強。

(2)通過配置使用@AspectJ切面

   1: <beans xmlns="http://www.springframework.org/schema/beans"
   2:        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3:        xmlns:p="http://www.springframework.org/schema/p"
   4:        xmlns:context="http://www.springframework.org/schema/context"
   5:        xmlns:aop="http://www.springframework.org/schema/aop"
   6:        xsi:schemaLocation="http://www.springframework.org/schema/beans
   7:                 http://www.springframework.org/schema/beans/spring-beans.xsd
   8:                 http://www.springframework.org/schema/context
   9:                 http://www.springframework.org/schema/context/spring-context-3.0.xsd
  10:                 http://www.springframework.org/schema/aop
  11:                 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
  12:  
  13:         <aop:aspectj-autoproxy/>
  14:         <bean class="com.wbl.AspectTest"/>

       首先,在配置文件中引入aop的命名空間,然后通過aop命名空間的<aop:aspectj-autoproxy/>自動為Spring容器中那些匹配@AspectJ切面的Bean創建代理,完成自動代理的創建工作。

    <aop:aspectj-autoproxy/>有一個proxy-target-class屬性,默認為false,表示使用JDK的動態代理織入增強,當配置為<aop:aspectj-autoproxy proxy-target-class=”true”/>時,表示使用CGLib的動態代理織入增強。
四. 利用基於@AspectJ的AOP實現權限控制
(1) 定義一個枚舉類
   1: public enum ACCESS {
   2:     LOOK,EXIT,DOWNLOAD,ADMIN
   3: }

該枚舉表示用戶擁有的權限,權限由低到高。

(2)定義一個注解

   1: @Retention(RetentionPolicy.RUNTIME)         //聲明注解的保留期限
   2: @Target(ElementType.METHOD)                 //聲明可以使用該注解的目標類型
   3: public @interface UserAccessAnnotation {    //定義注解
   4:     ACCESS value() default ACCESS.LOOK;     //聲明注解成員
   5: }

定義好這個注解之后,可以把注解放在需要進行權限控制的方法前

   1: public class DataOperate extends ActioinSupport{
   2:  
   3:     @UserAccessAnnotation(ACCESS.ADMIN)
   4:     public String updateData(String data){
   5:         System.out.println("Updata Data");
   6:     }
   7: }

       在這里我們使用 UserAccessAnnotation 來表示需要在updateData 方法執行之前判斷用戶的權限是否為管理員權限。

(3) 定義切點Pointcut

   1: public class SystemArchitecture {
   2:     /**
   3:      * A Join Point is defined in the action layer where the method needs
   4:      * a permission check.
   5:      */
   6:     @Pointcut("@annotation(com.wbl.aop.UserAccessAnnotation)")
   7:     public void userAccess(){}
   8: }

PointCut 即切入點,就是定義方法執行的點,before表示在方法執行前、after 表示方法執行后或者 around表示方法執行前后。 一般情況下,我們把 PointCut 全部集中定義在 SystemArchitecture 類中,以方便修改和管理。

(4) 定義一個切面

   1: @Aspect
   2: public class PermissionAspect {
   3:     @Before(value = "com.wbl.aop.SystemArchitecture.userAccess()&&" + 
   4:     "@annotation(userAccessAnnotation)",argNames="userAccessAnnotation")
   5:  
   6:     public void checkPermission(UserAccessAnnotation userAccessAnnotation) throws Exception{
   7:         User user = (User)ActionContext.getContext().getSession().get("user");
   8:         if(user != null){
   9:             if(user.getBehaviorLevel() < userAccessAnnotation.value().ordinal() + 1){
  10:                 throw new NoPermissionException("NO_ACCESS");
  11:             }
  12:         }
  13:     }
  14: }

argNames="userAccessAnnotation" 的意思是把 Annotation 當做參數傳遞進來,並判斷用戶的權限是否足夠。如果用戶的權限不足以訪問該方法,就要拋出 NoPermissionException,通知系統該用戶沒有權限。其中NoPermissionException是自定義的異常。

   1: public class NoPermissionException extends Exception {
   2:     public NoPermissionException(String msg){
   3:         super(msg);
   4:     }
   5: }

(5) 在applicationContext.xml中配置切面

   1: <aop:aspectj-autoproxy/>
   2: <bean class="com.wbl.aop.PermissionAspect"/>

(6) 在Struts中配置全局異常

   1: <global-results> 
   2:  <result name="error">/WEB-INF/jsp/error.jsp</result> 
   3:  </global-results> 
   4:  <global-exception-mappings> 
   5:  <exception-mapping exception="com.wbl.exceptions.NoPermissionException" 
   6:  result="loginerror"/> 
   7:  </global-exception-mappings>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM