實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
作者:一餐三碗
對于任何一個完整的應用系統,完善的認證和授權機制是必不可少的。在基于SpringFramework的WEB應用中,
我們可以使用Acegi作為安全架構的實現。本文將介紹如何在基于Spring構架的Web應用中使用Acegi,并且詳細介
紹如何配置和擴展Acegi框架以適應實際需要。
1. 概述
Acegi是一個專門為SpringFramework應用提供安全機制的開放源代碼項目,全稱為Acegi Security System for Spring,當前版本為
0.8.3。它使用了Spring的方式提供了安全和認證安全服務,包括使用Bean Context,攔截器和面向接口的編程方式。通過精心
配置Acegi安全系統能夠輕松地適用于復雜的安全需求。它既能應用于WEB應用也能應用于非WEB應用。在 本文的示例程序
里,我將演示如何將Acegi應用于WEB應用程序。通過這個例子詳細介紹如何配置Acegi的各個組件,同時介紹如何擴展Acegi
使其能夠從數據庫中讀取配置信息。
2. 例子說明
本文的例子是一個聯系人管理程序,使用SpringFramework 1.2.4 和 Acegi0.8.3,數據庫采用Mysql。程序的目錄結構如下:
acegi-sample
│ contactadd.jsp//增加聯系人頁面
│ contactedit.jsp//編輯聯系人頁面
│ contactlist.jsp//聯系人列表頁面
│ contactmainterance.jsp//聯系人操作頁面
│ index.jsp//主頁面
│ login.jsp//登錄頁面
│ logoff.jsp//登出頁面
│
├─WEB-INF
│ │ web.xml
│ │ applicationContext-basic.xml
│ │ applicationContext-security-acegi.xml
│ │ log4j.properties
│ │
│ ├─src
│ │ └─sample//java代碼目錄
│ ├─classes
│ └─lib//依賴包目錄
└─db//建表腳本目錄
程序的ER圖如下:
file:///I|/doc/index.htm (1 of 10)2005-12-6 10:37:51
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
說明:authorities用來存放用戶權限配置信息,其中,AUTHORITY字段存放以“AUTH_”開頭的權限標志串。AUTH_TYPE
存放權限類型。PROTECTED_RES字段存放受保護的資源字符串。DISPLAY字段存放權限顯示名稱。userinfo用來存放用戶信
息。authorities和userinfo是多對多關聯,使用user_auth作為關聯表。contacts存放用戶信息。 在這個例子中,為了使程序簡單明
了,我們沒有引入一般情況下會有的用戶組或者角色的概念,不過在后面的內容里我們將看到對現有的安全模型進行擴展是
一件很容易的事。
程序的靜態類圖如下:
3.配置文件說明
接下來,我們將進入本文的重要內容,開始對Acegi應用程序所牽涉到的配置文件進行一一說明。
3.1 web.xml
首先聲明SpringFramework的配置文件列表。為了便于管理,將業務方法相關的配置文件和Acegi安全配置相關的配置文件分
開。
file:///I|/doc/index.htm (2 of 10)2005-12-6 10:37:51
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-basic.xml
/WEB-INF/applicationContext-security-acegi.xml
</param-value>
</context-param>
其次聲明Acegi過濾器。
<!--Acegi Filter Chain Proxy -->
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
說明:Acegi對WEB應用的支持主要是依靠servlet 過濾器(filter) 來實現的。每一個http request都將被這些過濾器層層攔截 并
進行安全處理(包括認證和授權)。針對不同的安全處理,Acegi提供了不同的過濾器。過濾器的配置信息位于web.xml,但是
我們又希望把Acegi的過濾器配置信息放在SpringFramework的配置文件里(applicationContext-security-acegi.xml),從而實現對
這些過濾器的“控制反轉”。解決這個問題的方法是采用Acegi提供的FilterToBeanProxy。FilterToBeanProxy顧名思義就是對
Acegi過濾器Bean的代理,它的主要功能就是將http請求依次分派給對應的過濾器Bean。
3.2 applicationContext-security-acegi.xml
applicationContext-security-acegi.xml主要包括認證相關配置信息和WEB資源授權配置信息。首先是聲明過濾器序列。
<bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter
</value>
</property>
</bean>
各個過濾器的作用如下:
httpSessionContextIntegrationFilter
根據session中存放的信息組裝ContextHolder。ContextHolder主要用于存放SecureContext,
包括用戶的權限信息
authenticationProcessingFilter 處理認證請求(通常是一個登錄頁面的表單請求)
file:///I|/doc/index.htm (3 of 10)2005-12-6 10:37:51
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
anonymousProcessingFilter
匿名用戶處理。如果用戶尚未登錄,將生成一個匿名用戶的Authentication存放到
ContextHolder中
securityEnforcementFilter 強制安全驗證過濾器。驗證所請求的url是否在用戶的權限范圍內。
3.2.1 httpSessionContextIntegrationFilter相關配置信息
<bean id="httpSessionContextIntegrationFilter" class="net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter">
<property
name="context"><value>net.sf.acegisecurity.context.security.SecureContextImpl</value></property>
</bean>
說明: context屬性指定context的實現類。
3.2.2 authenticationProcessingFilter相關配置信息
authenticationProcessingFilter的配置比較復雜,我們通過下圖來看一下:
authenticationProcessingFilter配置說明:
file:///I|/doc/index.htm (4 of 10)2005-12-6 10:37:51
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
authenticationManager 認證管理器
authenticationFailureUrl 認證失敗后,重定向的url
defaultTargetUrl 認證成功后,重定向的url
filterProcessesUrl
該過濾器攔截的url,通常是/j_acegi_security_check,和登錄頁面(login.jsp)的登錄表單的action相
同
authenticationManager(認證管理器)用于管理AuthenticationProvider(認證提供者)。它的作用是使你能夠通過多個不同的認
證管理源來對用戶進行認證。認證管理器將依次調用認證提供者的認證方法,直到認證通過。本程序使用兩種認證提供者。
authenticationManager配置說明:
daoAuthenticationProvider 基于數據庫的認證提供者。
anonymousAuthenticationProvider 用于認證匿名用戶。
daoAuthenticationProvider主要功能是從數據庫取出用戶名和密碼,判斷登錄信息是否正確,如果是,則取出用戶權限等用戶
信息,并且存放到cache中,以便以后再次使用。具體流程可以參考:DaoAuthenticationProvider的authenticate方法。
daoAuthenticationProvider配置說明:
authenticationDao 認證數據訪問對象,用于獲取用戶信息,包括:用戶名,用戶密碼,用戶狀態和用戶權限。
userCache 用戶信息cache實現bean
jdbcDaoImpl是認證數據訪問對象,它能夠從默認的數據庫結構中獲取用戶信息,由于Acegi默認的數據庫結構和本程序的不
同,因此需要修改jdbcDaoImpl的默認sql。注意,采用這種方式能夠使Acegi很好的兼容舊的應用程序,因為它對底層的數據結
構并沒有強制要求。jdbcDaoImpl配置說明:
dataSource 數據源bean
usersByUsernameQuery 用戶信息查詢sql
authoritiesByUsernameQuery 用戶權限查詢sql
userCache用于定義用戶信息cache功能的提供者。userCache配置說明:
cache 定義ehcache工廠bean
本程序采用ehcache作為cache實現。由于認證管理器在每次對http請求進行認證之前都會查找用戶信息,通過使用cache就可以
避免每次都重復訪問數據庫。
3.2.3 anonymousProcessingFilter相關配置信息:
<bean id="anonymousProcessingFilter" class="net.sf.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key"><value>foobar</value></property>
<property name="userAttribute"><value>anonymousUser,AUTH_ANONYMOUS</value></property>
</bean>
說明:anonymousProcessingFilter的作用是判斷ContextHolder中是否有Authentication對象,如果沒有就創建一個Authentication對
象,其中包含的用戶名是anonymousUser,用戶權限是AUTH_ANONYMOUS。這使得沒有登錄的匿名用戶能夠自動的獲得匿
名的用戶名和權限。
3.2.4 securityEnforcementFilter相關配置信息
file:///I|/doc/index.htm (5 of 10)2005-12-6 10:37:51
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
securityEnforcementFilter的配置比較復雜,我們通過下圖來看一下:
securityEnforcementFilter配置說明:
filterSecurityInterceptor 實現對URL資源進行授權訪問。
authenticationEntryPoint 配置登錄界面信息。
securityEnforcementFilter的作用主要是將http請求轉發給filterSecurityInterceptor,由filterSecurityInterceptor來對HTTP請求的合法
性進行判斷。
filterInvocationInterceptor配置說明:
authenticationManager 認證管理器
accessDecisionManager 投票通過策略管理器
objectDefinitionSource
URL的權限配置信息。用于指定不同的URL資源對應的權限。配置如下:
/**/*.jpg=AUTH_ANONYMOUS,AUTH_USER
/**/*.gif=AUTH_ANONYMOUS,AUTH_USER
/**/*.png=AUTH_ANONYMOUS,AUTH_USER
/login.jsp*=AUTH_ANONYMOUS,AUTH_USER
/**=AUTH_USER
以上配置指定AUTH_ANONYMOUS權限的用戶(即匿名用戶)只可以訪問圖片資源和登錄頁
面,AUTH_USER權限的用戶可以訪問全部WEB資源。
accessDecisionManager(訪問決策管理器)首先通過authenticationManager判斷用戶是否通過認證(即是否已經登錄),然后根
據objectDefinitionSource的配置信息調用accessDecisionManager對用戶權限進行投票。
httpRequestAccessDecisionManager配置說明:
allowIfAllAbstainDecisions 設定是否允許:“沒人反對就通過”的投票策略
decisionVoters 投票者
file:///I|/doc/index.htm (6 of 10)2005-12-6 10:37:51
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
httpRequestAccessDecisionManager(投票通過策略管理器)用于管理投票通過策略。Acegi提供三種投票通過策略的實現:
AffirmativeBased(至少一個投票者同意方可通過),ConsensusBased(多數投票者同意方可通過),UnanimousBased(所有投
票者同意方可通過)。本程序采用AffirmativeBased策略,并且禁止“沒人反對就通過”的投票策略。
roleVoter配置說明:
rolePrefix
該投票者支持的權限前綴,默認是“ROLE_”,本程序所有的權限字符串均以“AUTH_”開頭,
故設為“AUTH_”
通過設定rolePrefix可以指定roleVoter所支持的權限范圍。
3.3 applicationContext-basic.xml
applicationContext-basic.xml主要包括數據訪問對象,業務方法,業務方法安全管理攔截器的配置信息。先看一下總攬圖:
我將主要講解業務方法安全管理攔截器(MethodSecurityInterceptor)的相關配置,其它的配置就不再贅述了,請參考相關文
檔。
3.3.1 contactManager相關配置信息
file:///I|/doc/index.htm (7 of 10)2005-12-6 10:37:51
實戰基于Spring框架的WEB應用的安全框架
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-basic.xml
/WEB-INF/applicationContext-security-acegi.xml
</param-value>
</context-param>
其次聲明Acegi過濾器。
<!--Acegi Filter Chain Proxy -->
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
說明:Acegi對WEB應用的支持主要是依靠servlet 過濾器(filter) 來實現的。每一個http request都將被這些過濾器層層攔截 并
進行安全處理(包括認證和授權)。針對不同的安全處理,Acegi提供了不同的過濾器。過濾器的配置信息位于web.xml,但是
我們又希望把Acegi的過濾器配置信息放在SpringFramework的配置文件里(applicationContext-security-acegi.xml),從而實現對
這些過濾器的“控制反轉”。解決這個問題的方法是采用Acegi提供的FilterToBeanProxy。FilterToBeanProxy顧名思義就是對
Acegi過濾器Bean的代理,它的主要功能就是將http請求依次分派給對應的過濾器Bean。
3.2 applicationContext-security-acegi.xml
applicationContext-security-acegi.xml主要包括認證相關配置信息和WEB資源授權配置信息。首先是聲明過濾器序列。
<bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter
</value>
</property>
</bean>
各個過濾器的作用如下:
httpSessionContextIntegrationFilter
根據session中存放的信息組裝ContextHolder。ContextHolder主要用于存放SecureContext,
包括用戶的權限信息
authenticationProcessingFilter 處理認證請 通常
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
4.程序演示
首先,配置兩個用戶:root,readonly。具有的權限如下圖:
登錄界面:
以readonly用戶登錄后,執行刪除聯系人操作,顯示操作被拒絕:
file:///I|/doc/index.htm (9 of 10)2005-12-6 10:37:51
實戰Acegi:使用Acegi作為基于Spring框架的WEB應用的安全框架
5.總結
在本文的示例程序中我們只對業務對象(ContactManager)進行安全保護,對業務領域對象(Contact)的訪問并沒有作限
制,這是由于在Acegi框架中采用ACL(訪問控制列表)技術實現這個功能,這使得一旦我們的業務領域對象數量很多的話,
效率將變得很低,因此我們將對業務領域對象訪問控制的代碼放在業務對象的業務方法中。
將業務無關的代碼從業務代碼中剝離,使業務代碼更干凈,系統結構更合理是每個開發人員的夢想。隨著AOP技術的日漸流
行和日益發展,這個夢想已經離我們不遠了。本文中的例子通過結合使用SpringFramework和Acegi兩種開源框架,實現了將安
全認證和授權代碼和事務代碼從業務代碼中分離。
6.代碼說明
本文相關代碼acegi-sample.war.
Mysql的建表腳本在db目錄下.
為了減小體積,已經將WEB-INF\lib下的依賴包刪除,請自行下載以下包,并拷貝至WEB-INF\lib下:
spring-1.2.4.jar
acegi-security-0.8.3.jar
aopalliance-1.0.jar
c3p0-0.9.0.jar
commons-logging-1.0.4.jar
ehcache-1.1.jar
log4j-1.2.8.jar
mysql-connector-java-3.1.10-bin.jar
oro-2.0.8.jar
7.參考資料
● Acegi Security System for Spring Reference Documentation。
● Acegi Security System contacts sample。本文中的示例程序參考了Acegi的contacts演示程序。
關于作者
一餐三碗 系統架構師 從事電信行業軟件開發多年,目前專注于網管軟件的開發。
file:///I|/doc/index.htm (10 of 10)2005-12-6 10:37:51