關於AOP攔截器使用的一個問題,尋求解決方案

johnnylzb發表於2008-02-19
最近我在設計通用許可權元件的時候遇到AOP攔截器方面的一個問題,尋求解決方案,先描述一下應用場景:

通用許可權元件以AOP的形式“插入”業務系統,AOP的攔截機制有兩種:
(A)透過Filter對使用者請求的URI進行攔截
(B)透過Interceptor對使用者呼叫的服務介面進行攔截

例子如下:
某業務系統有A、B兩大模組,分別提供兩大型別的服務,A、B兩個模組都包含了若干的服務介面方法:

public interface AService {
public void a1() throws ServiceException;

public void a2() throws ServiceException;
}

public interface BService {
public void b1() throws ServiceException;

public void b2() throws ServiceException;
}

為了實現以Interceptor的方式進行服務介面方法攔截,以實現許可權控制,需求在許可權系統把每個服務介面方法都進行註冊,使其成為受控資源實體:

com.xxx.AService.a1
com.xxx.AService.a2
com.xxx.BService.b1
com.xxx.BService.b2

然後把這些實體分配給角色,例如:

Role1:com.xxx.AService.a1
Role2:com.xxx.AService.a1;com.xxx.AService.b1

問題出現了,請看服務的實現類

public class AServiceImpl implements AService {
private BService bService;

public void a1() thorws ServiceException() {
// Business logic
bService.b1();
// Other business logic
...
}
...
}

由於服務A.a1的某些業務邏輯與服務B.b重複了,合理的設計是,把B作為成員變數注入到A中,並且A以介面的方式使用B(如上述程式碼片斷),但由於實際的需要,某個角色Role1只能訪問a1服務,無權訪問b1服務,由於使用了AOP的攔截器方式,Role1在訪問AService.a1()的時候,攔截器把這個動作攔截下來,判斷Role1發現他擁有AService.a1()的呼叫權,本次攔截透過,Role1順利的呼叫了a1()方法,但隨即,a1()呼叫了BService.b1()方法,這時候攔截器再次對Role1進行許可權判斷,發現Role1並沒有b1()的呼叫權,最終,本次呼叫被拒絕。但實際上,從使用者的角度考慮,使用者不應該知道實現細節,他在配置許可權的時候,只關心業務,我們不可能要求使用者要從技術角度去知道a1本身呼叫了b1。

上述的情況不常見,因為如果使用者擁有a1的使用權,而a1本身又需要b1提供服務,正常的情況下該使用者應該同時擁有a1、b1的使用權,但事實可能真的會存在上述的情況,我想到的其中一種辦法是:

在AService、BService的“前面”增加一個Facade(門面),許可權攔截器攔截這個Facade,而不是攔截直接AService和BService。

其實這樣的設計是合理的,雖然為系統增加了一個“層”,但這個“層”剛好把系統抽象的分離為“對外服務介面”和“對內服務介面”了,服務之間的呼叫,直接使用Service介面,外部訪問系統,使用Facade介面,整個系統的設計更加合理和清晰,靈活性也更強。

但同時我又必須考慮一個問題:我設計的是一個通用的許可權元件,為了儘可能通用,元件必須能最大限度適應外界的各種情況,而且,通用元件不可能要求外部系統必須要使用這種“Facade Pattern”,如果是這樣的話,不但加大了對外部使用者的限制,而且會讓外部系統與許可權元件形成“迴圈依賴”,因為許可權元件不得不依賴於外部系統提供Facade。

本人對AOP的概念理解或者算是比較清晰,但對於AOP的技術實現(Spring AOP、AspectJ等)並不精通,請教一下各位,有沒有更好的解決方案呢?

[該貼被johnnylzb於2008-02-19 17:21修改過]

相關文章