Spring Security 安全對象實現
1. 前言
上節我們討論了如何利用表達式簡化權限規則的配置,本節將討論對象級的權限管理。
安全對象(Security Object)指任何可以有安全限制的對象,常見的比如方法調用,或者 Web 請求等。
每一個安全對象都有自己的一個攔截器實現。
本節,我們將討論安全對象的鑒權如何實現。
2. AOP 安全攔截器
在 Spring Security 2.0 之前,想要對「安全對象」進行保護,需要使用大量的「樣板模式」來實現。
所謂樣板模式,就是在父類(抽象類)中公開定義某對象的執行方法,該方法可以被其子類重寫,但是該方法的調用需要在父類的方法中完成,也就是由父類決定行為,由子類決定過程細節,封裝不變的部分,擴展可變的部分。
在新版本的 Spring Security 中,采用了「基于命名空間配置」的方式實現安全保護,通過這種方式,Bean 對象可以自動配置出安全策略,而不必過多關注細節。
這其中主要涉及了以下一些類:
方法的安全性通過 MethodSecurityInterceptor
實現,用來保護方法執行過程的安全。攔截器可以配置為指定單一的 Bean 對象或者在多個 Bean 對象之間共享,它使用 MethodSecurityMetadataSource
實例里維護配置屬性,用來應用到一個具體的方法調用上。MapBasedMethodSecurityMetadataSource
實例用于儲存那些以方法名為主鍵的配置屬性,并且當屬性在應用上下文中被用于 <intercept-methods>
或 <protect-point>
元素時會在內部直接調用這些屬性。其他實現則基于注釋的配置。
我們可以在應用上下文中顯示的配置 Spring AOP 攔截器。
例如:
<bean id="bankManagerSecurity" class=
"org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<sec:method-security-metadata-source>
<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
</sec:method-security-metadata-source>
</property>
</bean>
3. AspectJ 安全攔截器
AspectJ 安全攔截器和 AOP 聯盟安全攔截器類似,但仍有一些不同。
AspectJ 安全攔截器的應用類名為 AspectJSecurityInterceptor
。不同于 AOP 聯盟安全攔截器,它不是基于 Spring 應用上下文來激活攔截器,它通過 AspectJ 編譯器實現。多數情況下,同一應用會出現這兩種安全攔截器,AspectJ 用于域對象的安全控制,AOP 聯盟安全攔截器用于服務層的安全。
AspectJSecurityInterceptor
的配置方式如下:
<bean id="bankManagerSecurity" class=
"org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<sec:method-security-metadata-source>
<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
</sec:method-security-metadata-source>
</property>
</bean>
可見,除了類名之外,AspectJ 方式與 AOP 聯盟方式配置幾乎一樣。不僅如此,這兩個攔截器可以共用 securityMetadataSource
對象。
下一步,我們需要定義 AspectJ 的 aspect
,例如:
package org.springframework.security.samples.aspectj;
import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.AspectJCallback;
import org.springframework.beans.factory.InitializingBean;
public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
private AspectJSecurityInterceptor securityInterceptor;
pointcut domainObjectInstanceExecution(): target(PersistableEntity)
&& execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect);
Object around(): domainObjectInstanceExecution() {
if (this.securityInterceptor == null) {
return proceed();
}
AspectJCallback callback = new AspectJCallback() {
public Object proceedWithObject() {
return proceed();
}
};
return this.securityInterceptor.invoke(thisJoinPoint, callback);
}
public AspectJSecurityInterceptor getSecurityInterceptor() {
return securityInterceptor;
}
public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
public void afterPropertiesSet() throws Exception {
if (this.securityInterceptor == null)
throw new IllegalArgumentException("securityInterceptor required");
}
}
}
這段代碼中,安全攔截器被應用每一個 PersistableEntity
實例。AspectJCallback
被用于執行 proceed()
,該調用只有在 around()
方法中才能得以執行,當我們需要目標對象繼續執行時,這些匿名的回調函數會被調用。
下一步,我們需要配置 Spring 加載 aspect 并關聯到 AspectJSecurityInterceptor
攔截器中,如下:
<bean id="domainObjectInstanceSecurityAspect"
class="security.samples.aspectj.DomainObjectInstanceSecurityAspect"
factory-method="aspectOf">
<property name="securityInterceptor" ref="bankManagerSecurity"/>
</bean>
到這里為止,我們可以隨意的創建自己的 bean 對象了,他們都將被安全攔截器覆蓋。
4. 小結
本節討論了對象級的安全配置策略,主要內容有:
- Spring Security 中的安全對象指所有需要被安全限制的對象;
- Spring Security 通過配置攔截器的方式保護安全對象不受非法訪問;
- Spring Security 的安全對象攔截器分為 AOP 方式和 AspectJ 方式,兩種方式的執行時期不同,前置在程序運行過程中動態啟用,后者在編譯時靜態啟用;
- 兩種攔截器并不影響,通常可以一起使用。
下節我們討論「域對象」的權限配置。