SpringSide 3 中的安全框架

SpringSide 3 中的安全框架 原文來至:http://ljx1619.iteye.com/blog/468166 在SpringSide 3的官方文檔中,說安全框架使用的是Spring Security 2.0。乍一看,嚇了我一跳,以爲Acegi這麼快就被淘汰了呢。上搜索引擎一搜,發現原來Spring Security 2.0就是Acegi 2.0。懸着的心放下來了。雖然SpringSide 3中關於Acegi的配置文件看起來很不熟悉,但是讀了Acegi 2.0的官方文檔後,一切都釋然了。 先來談一談Acegi的基礎知識,Acegi的架構比較複雜,但是我希望我下面的隻言片語能夠把它說清楚。大家都知道,如果要對Web資源進行保護,最好的辦法莫過於Filter,要想對方法調用進行保護,最好的辦法莫過於AOP。Acegi對Web資源的保護,就是靠Filter實現的。如下圖: 一般來說,我們的Filter都是配置在web.xml中,但是Acegi不一樣,它在web.xml中配置的只是一個代理,而真正起作用的Filter是作爲Bean配置在Spring中的。web.xml中的代理依次調用這些Bean,就實現了對Web資源的保護,同時這些Filter作爲Bean被Spring管理,所以實現AOP也很簡單,真的是一舉兩得啊。 Acegi中提供的Filter不少,有十多個,一個一個學起來比較複雜。但是對於我們Web開發者來說,常用的就那麼幾個,如下圖中的被紅圈圈標記出來的: 從上到下,它們實現的功能依次是1、制定必須爲https連接;2、從Session中提取用戶的認證信息;3、退出登錄;4、登錄;5、記住用戶;6、所有的應用必須配置這個Filter。 一般來說,我們寫Web應用只需要熟悉這幾個Filter就可以了,如果不需要https連接,連第一個也不用熟悉。但是有人肯定會想,這些Filter怎麼和我的數據庫聯繫起來呢?不用着急,這些Filter並不直接處理用戶的認證,也不直接處理用戶的授權,而是把它們交給了認證管理器和決策管理器。如下圖: 對於這兩種管理器,那也是不需要我們寫代碼的,Acegi也提供了現成的類。那麼大家又奇怪了:又是現成的,那怎麼和我的數據庫關聯起來呢?彆着急,其實這兩個管理器自己也不做事,認證管理器把任務交給了Provider,而決策管理器則把任務交給了Voter,如下圖: 現在我要告訴你們,這裏的Provider和Voter也是不需要我們寫代碼的。不要崩潰,快到目標了。Acegi提供了多個Provider的實現類,如果我們想用數據庫來儲存用戶的認證數據,那麼我們就選擇DaoAuthenticationProvider。對於Voter,我們一般選擇RoleVoter就夠用了,它會根據我們配置文件中的設置來決定是否允許某一個用戶訪問制定的Web資源。 而DaoAuthenticationProvider也是不直接操作數據庫的,它把任務委託給了UserDetailService,如下圖: 而我們要做的,就是實現這個UserDetailService。圖畫得不好,大家不要見笑,但是說了這麼多總算是引出了我們開發中的關鍵,那就是我們要實現自己的UserDetailService,它就是連接我們的數據庫和Acegi的橋樑。UserDetailService的要求也很簡單,只需要一個返回org.springframework.security.userdetails.User對象的loadUserByUsername(String userName)方法。因此,怎麼設計數據庫都可以,不管我們是用一個表還是兩個表還是三個表,也不管我們是用戶-授權,還是用戶-角色-授權,還是用戶-用戶組-角色-授權,這些具體的東西Acegi統統不關心,它只關心返回的那個User對象,至於怎麼從數據庫中讀取數據,那就是我們自己的事了。 反過來再看看上面的過程,我們發現,即使我們要做的只是實現自己的UserDetailService類,但是我們不得不在Spring中配置那一大堆的Bean,包括幾個Filter,幾個Manager,幾個Provider和Voter,而這些配置往往都是重複的無謂的。好在Acegi 2.0也認識到了這個問題,所以,它設計了一個<http>標籤,讓Acegi的配置得到了簡化。下面是SpringSide 3中的配置的截圖,大家可以看看: 下圖是官方文章中的傳統Filter設置和<http>元素之間的對應關係: 下面的代碼是SpringSide 3中實現UserDetailService的範例,在SpringSide 3的範例中,白衣使用了三個表User、Role、Authority。但是Acegi不關心你用了幾個表,它只關心UserDetails對象。而決定用戶能否訪問指定Web資源的,是RoleVoter類,無需任何修改它可以工作得很好,唯一的缺點是它只認ROLE_前綴,所以搞得白衣的Authority看起來都象角色,不倫不類。 package personal.youxia.service.security; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Required; import org.springframework.dao.DataAccessException; import org.springframework.security.GrantedAuthority; import org.springframework.security.GrantedAuthorityImpl; import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UserDetailsService; import org.springframework.security.userdetails.UsernameNotFoundException; import personal.youxia.entity.user.Authority; import personal.youxia.entity.user.Role; import personal.youxia.entity.user.User; import personal.youxia.service.user.UserManager; /** * 實現SpringSecurity的UserDetailsService接口,獲取用戶Detail信息. * * @author calvin */ public class UserDetailServiceImpl implements UserDetailsService { private UserManager userManager; public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException { User user = userManager.getUserByLoginName(userName); if (user == null ) throw new UsernameNotFoundException(userName + " 不存在 " ); List authsList = new ArrayList (); for (Role role : user.getRoles()) { for (Authority authority : role.getAuths()) { authsList.add( new GrantedAuthorityImpl(authority.getName())); } } // 目前在MultiDatabaseExample的User類中沒有enabled, accountNonExpired,credentialsNonExpired, accountNonLocked等屬性 // 暫時全部設爲true,在需要時才添加這些屬性. org.springframework.security.userdetails.User userdetail = new org.springframework.security.userdetails.User( user.getLoginName(), user.getPassword(), true , true , true , true , authsList .toArray( new GrantedAuthority[authsList.size()])); return userdetail; } @Required public void setUserManager(UserManager userManager) { this .userManager = userManager; } } 最後再來說說這個命名的問題,我對Authentication和Authority這兩個單詞比較反感,兩個原因,一是因爲它們太生僻了,二是因爲它們長得太像了,明明一個是認證,一個是授權,意思相差很遠,外貌卻如此相似,確實很煩人。如果讓我來選擇,我喜歡Privilege這個單詞,在我剛使用MySQL的時候就跟它很熟了,所以在我的項目中,我可能會用Privilege來代替Authority。如果我們只使用User-Role兩級關係,使用RoleVoter默認的ROLE_前綴當然沒有關係,如果是像白衣這樣是用三層關係,最好還是把這個前綴改一改,以免混淆。 補充: 前文所講的是我對Acegi的一些理解,我認爲只有把條理搞清楚了,才更容易深入。我想得比較簡單,當然會漏掉一些細節。這裏把它補充一下。 1、我上面講到的主要內容,包含了認證和授權,但是漏掉了資源,資源就是我們需要保護的URL,或者一些類中的方法。要保護URL,在xml文件中按照前面的例子配置就可以了,要保護類中的方法,使用@secured就可以了。 但是它們是和Acegi中的哪個組件關聯起來的呢?是FilterSecurityInterceptor和MethodSecurityInterceptor,這兩個Interceptor都需要設置一個叫objectDefinitionSource的屬性。所以,有人要問,如何把對資源的保護設置全部轉移到數據庫中,避免寫在xml中,那就要從這個objectDefinitionSource着手了。 2、前面講到了UserDetailService,事實上在Acegi中還需要配置別的Service,如RememberMeService,當然,該Service也是現成的,不需要我們寫代碼的,RememberMeProcessingFilter需要依賴這個Service。 3、Acegi支持OpenID和CAS 3,這兩個東西是幹什麼的呢?是可以實現單點登錄功能的,也就是允許用戶只登錄一次,就可以使用多個網站。這對於那些很龐大的網站非常有用,可以把用戶登錄這樣的操作集中在一臺服務器上。要使用CAS,只需要配置Filter時選擇CASProcessingFilte,配置Provider時選擇CasAuthenticationProvider,其餘的概念都是相通的。具體的實現細節,大家慢慢摸索吧。 </http></http>
發佈了25 篇原創文章 · 獲贊 1 · 訪問量 1802
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章