初涉AOP

AOP下的權限控制實現

  摘要 面向方面的編程(AOP)是一種新的編程技術,它彌補了面向對象的編程(OOP)在跨越模塊行爲上的不足。AOP引進了Aspect,它將影響多個類的行爲封裝到一個可重用模塊中,它允許程序員對橫切關注點進行模塊化,從而消除了OOP引起的代碼混亂和分散問題,增強了系統的可維護性和代碼的重用性。本文分析傳統權限控制的實現方法,並研究了在AOP下權限控制的實現方法。

  關鍵詞 AOP;橫切關注點; 設計模式; 權限控制

  OOP應用開發面臨的問題

  面向對象技術很好地解決了軟件系統中角色劃分的問題。藉助於面向對象的分析、設計和實現技術,開發者可以將問題領域的“名詞”轉換成軟件系統中的對象,從而很自然地完成從問題到軟件的轉換。

  但是,問題領域的某些需求卻偏偏不是用這樣的“名詞”來描述的。比如遇到這樣的問題:需要對系統中的某些方法進行權限檢驗,這種需要權限檢驗的方法散佈在40多個類中。面對這種需求,應該怎麼辦呢?最直接的辦法就是:創建一個起類(或接口),將權限檢驗的功能放在其中,並讓所有需要權限檢驗的類繼承這個起類(或接口).如果這個需求是後期提出的.需要修改的地方就會分散在40多個文件中。這樣大的修改量,無疑會增加出錯的機率,並且加大系統維護的難度。

  人們認識到,傳統的程序經常表現出一些不能自然地適合單個程序模塊或者幾個緊密相關的程序模塊的行爲例如權限檢驗、日誌記錄、對上下文敏感的錯誤處理、性能優化以及設計模式等等、我們將這種行爲稱爲“橫切關注點(crosscuttingconcern)”,因爲它跨越了給定編程模型中的典型職責界限。如果使用過用於橫切關注點的代碼,您就會知道缺乏模塊性所帶來的問題。因爲橫切行爲的實現是分散的,開發人員發現這種行爲難以作邏輯思維、實現和更改。

  AOP的基本思想

  AOP是Aspect Oriented Programming的縮寫,意思是面向方面編程,一種新興的編程技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP可以說也是這種目標的一種實現。它可以解決OOP和過程化方法不能夠很好解決的橫切(crosscut)問題,如:事務、安全、日誌等橫切關注。當未來系統變得越來越複雜,橫切關注點就成爲一個大問題的時候,AOP就可以很輕鬆的解決橫切關注點這個問題。


圖 1 把模塊作爲一批關注點來實現

  通常,爲滿足整個企業應用某方面得需求,開發者(架構師)需要整理出系統得關注點。圖 1形象地描述了關注點,它能夠從AOP Aspect角度看待系統。比如,持久化、日誌、應用的業務邏輯通常被認爲是應用需要解決的問題。因此,他們通常作爲關注點看待。從整個系統角度考慮,它往往是由大量的關注點構成的。

  我們把AOP看作是OOP的延續,而不是競爭對手。OOP在通常的場合下工作得很好,但在特定的領域裏卻有所欠缺:舉例來說,如果我們必須爲多個對象和方法應用相同的事務行爲,我們需要將同樣的代碼剪切/粘貼到每一個方法裏。AOP讓我們可以把這類問題封裝到方面(aspect)中,從而更好地實現模塊化。AOP定義了“切入點”(pointcut)的概念,讓開發者可以從另一個角度來思考程序的結構,從而彌補了OOP的某些缺陷:如果需要對一組方法施加橫切的行爲,就應該攔截這些方法。

  在J2EE應用開發中,我們主要用到AOP的攔截(interception)能力,它爲我們提供了“在任何對象的方法調用前/後加入自定義行爲”的能力,這使得我們可以處理企業應用中的橫切(crosscutting)關注點(即:同時作用於多個對象的關注點),並且仍然保持強類型(不需要改變方法簽名)。

  權限控制的應用程序實現

  對於權限管理的做法,在WEB實現上,有以下幾種:

  ⑴ 利用Filter,對所有進入的URI進行解析,並取得當時Session中的User信息,然後通過RBAC的機制,將此鏈接需要的權限與用戶擁有的權限進行比較,然後進行相應的處理。這種做法有很多好處:簡單,容易實現,並且對系統侵入性也不強。這裏URL就是RBAC中的資源了。這樣做的缺點是所有對數據的操作必須通過URL來體現,這一點在現代的程序中不太好實現。如果採用Struts, XWork或者Tapestry,採用同一個URL(瀏覽器看來)進行處理多項任務已不是什麼稀奇的事。

  ⑵ 利用一個BaseServlet(Servlet+Jsp經典模式)或者BaseAction(Struts模式)或者BasePage(Tapestry模式)或者BaseController(SpringMVC模式),對所有的請求先進行過濾進行權限操作,然後再處理。稍微看一下就知道這種模式跟Filter並無本質不同。優缺點同上。

  那麼,如果要實現更爲細緻的權限操作,精確到某個方法的權限,典型的做法如下:

public someFunciton() {
 //權限判斷
 User user = context.getUser();
 if (user.canExecuteThisFunction()) {
  // do the business method
  // ...
 } else {
  throw new PermissionDeniedException();
 }
}

  這種做法能夠將權限的粒度控制到具體的業務方法,因此它的控制能力應該是強大的。可以看到,權限判斷部分對於每個方法幾乎是獨立的。

  這種在具體功能前加入權限操作檢驗的實現方式有很多缺點:

  ⑴ 每個功能類都需要相應的權限檢驗代碼,將程序功能和權限檢驗混淆在一起,存在緊密的耦合性,擴展修改難度大。

  ⑵ 以代理模式爲每個功能類實現一個相應的代理類,雖然解耦了程序功能和權限檢驗,但是,從某個角色的權限檢驗這個切面考慮,涉及具體Proxy類太多,擴展修改難度大。

  權限控制的J2EE容器實現

  在AOP概念沒有誕生前,J2EE規範已經提供了關於權限控制的容器實現標準,這種變遷結果如下圖所示:


圖2 權限控制的J2EE容器實現

  原來需要每個應用程序實現的權限Proxy轉爲整個容器的Proxy實現,其中JDK1.3以後的動態代理API爲這種轉換實現提供了技術保證。

  非常明顯,通過容器實現權限控制驗證可以大大簡化應用程序的設計,分離了應用系統的權限關注,將權限控制變成了對J2EE容器服務器的配置工作。

  其實,容器的權限實現也是一種從一個側面來解決問題方式,AOP概念誕生後,權限控制實現由此也帶來了兩個方向的變化:

  ⑴ J2EE容器級別的權限實現,也就是容器自身的權限實現。

  ⑵ J2EE應用程序級別的權限實現。

  權限控制在容器級別實現似乎使得J2EE開發者感覺沒有靈活性和可擴展性,其實象JBoss 4.0這樣的J2EE容器,由於引入了AOP概念,使得J2EE開發者在自己的應用系統中能夠直接操縱容器的一些行爲。容器和應用系統由於AOP引入的Aspect切面,變得可以成爲一體了。

  對於J2EE應用系統開發者,能夠做到上述境界,必須的條件是對JBoss之類J2EE容器必須有足夠的瞭解,因爲這些方式並不是J2EE標準,有可能在移植到新的J2EE容器,這些知識和投入變得無用(也有可能將來J2EE擴展其標準)。

  很顯然,使用AOP實現J2EE應用系統級別的權限控制,是解決上述移植風險的一個主要方法,但是帶來的缺點是必須親自從零開始做起,耗費時間不會很短。

  AOP下的權限控制實現

  有了AOP,新的業務方法可以這樣寫:

public someFunciton() {
 // do the business method
 // ...
}

  沒有了額外的權限操作,這個業務方法看起來那麼清晰自然。

  將對權限的操作作爲一個Advice,並將Advisor關注到所有的業務方法(可能有某一個特定package),然後,剩下的事情就由RBAC以及AOP來完成了。通過這樣的分離,縱向的一個業務方法被分割爲一個更爲自然的業務方法和一個關注點。這個關注點寫法可能如下:

public class PermissionCheckAdvice implements MethodBeforeAdvice {
 public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
  //權限判斷
  if (!this.getContext().getUser().canExcute(this, arg0)) {
   throws new PermissionDeniedException();
  }
 }
}

  可能有個問題:如何取得context或者當時上下文環境的User呢?答案是使用IoC(或稱Dependency Injection),將上下文環境或者User作爲參數反向傳入到邏輯方法中。當然,在傳入之前,這些變量是需要初始化的。這個初始化工作可以在SuperServlet中進行,並且以Session單例的形式保存在應用程序中。下面是Spring配置文件的例子:

<beans>
<!-- Bean configuration -->
<bean id="businesslogicbean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IBusinessLogic</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>thePermissionCheckBeforeAdvisor</value>
</list>
</property>
</bean>
<!-- Bean Classes -->
<bean id="beanTarget" class="BusinessLogic">
<property name="user"><<YOUR USER OBJECT>> </property>
</bean>
<!-- Advisor pointcut definition for before advice -->
<bean id="thePermissionCheckBeforeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="thePermissionCheckBeforeAdvice"/>
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean>
<!-- Advice classes -->
<bean id="thePermissionCheckBeforeAdvice"
class="PermissionCheckBeforeAdvice"/>
</beans>

  結束語

  AOP引進了Aspect,它將影響多個類的行爲封裝到一個可重用模塊中,它允許程序員對橫切關注點進行模塊化,從而消除了OOP引起的代碼混亂和分散問題,增強了系統的可維護性和代碼的重用性。利用AOP的攔截(interception)能力,它爲我們提供了“在任何對象的方法調用前/後加入自定義行爲”的能力,這使得我們可以處理企業應用中的權限控制這一橫切關注點,並且仍然保持強類型(不需要改變方法簽名),解耦了程序功能和權限檢驗。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章