京山游俠

          專注技術,拒絕扯淡
          posts - 50, comments - 868, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          SpringSide 3 中的安全框架

          Posted on 2008-12-07 19:41 京山游俠 閱讀(65764) 評論(29)  編輯  收藏 所屬分類: SpringSide開發實戰

          在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實現的。如下圖:
          001.PNG

          一般來說,我們的Filter都是配置在web.xml中,但是Acegi不一樣,它在web.xml中配置的只是一個代理,而真正起作用的Filter是作為Bean配置在Spring中的。web.xml中的代理依次調用這些Bean,就實現了對Web資源的保護,同時這些Filter作為Bean被Spring管理,所以實現AOP也很簡單,真的是一舉兩得啊。

          Acegi中提供的Filter不少,有十多個,一個一個學起來比較復雜。但是對于我們Web開發者來說,常用的就那么幾個,如下圖中的被紅圈圈標記出來的:
          002.PNG

          從上到下,它們實現的功能依次是1、制定必須為https連接;2、從Session中提取用戶的認證信息;3、退出登錄;4、登錄;5、記住用戶;6、所有的應用必須配置這個Filter。

          一般來說,我們寫Web應用只需要熟悉這幾個Filter就可以了,如果不需要https連接,連第一個也不用熟悉。但是有人肯定會想,這些Filter怎么和我的數據庫聯系起來呢?不用著急,這些Filter并不直接處理用戶的認證,也不直接處理用戶的授權,而是把它們交給了認證管理器和決策管理器。如下圖:
          003.PNG

          對于這兩種管理器,那也是不需要我們寫代碼的,Acegi也提供了現成的類。那么大家又奇怪了:又是現成的,那怎么和我的數據庫關聯起來呢?別著急,其實這兩個管理器自己也不做事,認證管理器把任務交給了Provider,而決策管理器則把任務交給了Voter,如下圖:
          004.PNG

          現在我要告訴你們,這里的Provider和Voter也是不需要我們寫代碼的。不要崩潰,快到目標了。Acegi提供了多個Provider的實現類,如果我們想用數據庫來儲存用戶的認證數據,那么我們就選擇DaoAuthenticationProvider。對于Voter,我們一般選擇RoleVoter就夠用了,它會根據我們配置文件中的設置來決定是否允許某一個用戶訪問制定的Web資源。

          而DaoAuthenticationProvider也是不直接操作數據庫的,它把任務委托給了UserDetailService,如下圖:
          005.PNG

          而我們要做的,就是實現這個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中的配置的截圖,大家可以看看:
          006.PNG

          下圖是官方文章中的傳統Filter設置和<http>元素之間的對應關系:
          007.PNG

          下面的代碼是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
          < GrantedAuthority > ?authsList? = ? new ?ArrayList < GrantedAuthority > ();

          ????????
          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_前綴當然沒有關系,如果是像白衣這樣是用三層關系,最好還是把這個前綴改一改,以免混淆。

          評論

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-08 10:15 by 楊愛友
          以前看過這個安全框架,覺得配置太復雜,放棄使用了,今天叫你說的這么簡單,回頭再看一下。

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-08 11:12 by regale
          謝謝!

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-08 11:17 by leekiang
          挺奇怪的,權限的前綴為什么要用"ROLE_"呢,我覺得用"PRIVILEGE_"或者"AUTHORITY_"比較恰當。

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-08 20:11 by 火線生存
          講得非常通俗,對Spring Security又理解一層了。非常感謝!

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-10 08:46 by 淘聲依舊
          簡單多了

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-11 22:33 by 有點不明白!
          這些是簡化了配置,不過我還是不明白, 如何把 所有的權限和角色都定義在數據庫中,常常在開發中 角色是可以維護的, 而且 較大的項目 權限這樣配置工作量太大。有沒有什么好的辦法。

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-24 13:15 by 海邊沫沫
          補充:

          前文所講的是我對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,其余的概念都是相通的。具體的實現細節,大家慢慢摸索吧。

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-24 15:44 by 虎嘯龍吟
          講的比較清楚、透徹!但有幾個問題:
          IS_AUTHENTICATED_ANONYMOUSLY、ROLE_MODIFY_USER等 有什么作用?由誰定義啊?

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2008-12-24 20:16 by 海邊沫沫
          @虎嘯龍吟
          這些是我們自己定義的。用戶、角色、授權都是開發者自己定義的,在SpringSide中,這些東西是用數據庫保存的,而資源和授權的關系,是定義在xml文件中的,就是你看到的配置文件的內容。

          # 沫沫幫忙看看這個文題吧,幫忙解決一下吧  回復  更多評論   

          2009-01-06 16:45 by playingfly
          沫沫幫忙看看這個文題吧,也是關于安全認證登陸的,幫忙解決一下吧 ,十分感謝了?。?!

          http://forum.springside.org.cn/viewthread.php?tid=3352&extra=page%3D1

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2009-01-12 19:18 by 江南白衣
          ROLE_的前綴,貌似不大容易改掉啊,誰改過的告訴一下方法,的確很容易讓人混淆。

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2009-01-13 16:58 by yzl45
          在這里要強調一點,acegi已經不存在了,在1.0X以后,就叫做Spring Security了,這個是官方的正式更名。
          “ROLE_”這個前綴是Spring Security一個默認的前綴,如果不想用,可以替換它,提換的方法是:
          <beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
          <beans:property name="allowIfAllAbstainDecisions" value="false"/>
          <beans:property name="decisionVoters">
          <beans:list>
          <beans:bean class="org.springframework.security.vote.RoleVoter">
          <beans:property name="rolePrefix" value="替換成你想要的前綴"></beans:property>
          </beans:bean>
          <beans:bean class="org.springframework.security.vote.AuthenticatedVoter"/>
          </beans:list>
          </beans:property>
          </beans:bean>

          # re: SpringSide 3 中的安全框架[未登錄]  回復  更多評論   

          2009-01-14 00:20 by 江南白衣
          SpringSide的SVN里ROLE_已經改成AUTH_了 :)

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2009-02-01 11:22 by mojiezhong
          實現“用戶-用戶組-角色-授權”四級關系與“用戶-角色-授權”關系難度大嗎?性能影響如何?

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2009-05-08 16:00 by 小k

          很久以前試用了一下acegi1.x版本,,用戶 角色== 信息都放數據庫里面來,,

          但是問題來了.我想做動態權限,這個玩意是使用別人的系統的時候看到的...

          如果使用的acegi 但標簽字頁面控制了顯示,那么還有辦法讓角色也動態么??

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2012-04-13 09:05 by 熊錦鵬
          斷斷續續 看了好久的這個 直到今天才明白了一點 多謝LZ啊
          不過還有一個問題,就是取到的權限,怎么用呢,就是List < GrantedAuthority > authsList 這個怎么用,是不是這個集合里放的就是菜單的集合?怎么用?

          # re: SpringSide 3 中的安全框架[未登錄]  回復  更多評論   

          2012-09-12 17:03 by lsysbl
          寫的太好了。我在網上看了幾十篇,都沒有看懂。就你這篇我理解了。你真人才。

          # re: SpringSide 3 中的安全框架[未登錄]  回復  更多評論   

          2013-01-05 20:22 by alex
          我看了幾天的spring security都不理解,直到看完你的文章,終于大悟了。真是非常感激。

          # re: SpringSide 3 中的安全框架[未登錄]  回復  更多評論   

          2013-01-25 12:45 by lh
          非常感謝,只能用一個大徹大悟來形容了

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2013-05-27 21:09 by 吳彥欣
          是我看到最詳細的spring security介紹了,懂了好多,謝謝LZ

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2013-08-30 09:01 by geek
          好文章,支持樓主?。。?!

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2014-02-10 15:27 by 845885222@qq.com
          講解還不錯的

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2014-02-25 10:32 by 殘風
          謝謝樓主分享

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2014-04-14 16:34 by cl
          謝謝分享

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2015-05-14 11:22 by 1057718341
          通俗易懂,寫的非常好,感謝樓主分享。

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2015-05-19 17:51 by gfbg
          loil

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2015-05-19 17:53 by gfbg
          說了一大堆,我沒怎么看懂

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2015-09-18 00:10 by
          樓主的文章果然講的好清晰,謝謝分享

          # re: SpringSide 3 中的安全框架  回復  更多評論   

          2017-05-10 11:32 by 薄荷檸檬草
          通俗易懂,謝謝樓主
          主站蜘蛛池模板: 庆安县| 边坝县| 贡嘎县| 拜城县| 华蓥市| 南昌县| 洛隆县| 左贡县| 共和县| 南宫市| 滕州市| 桂平市| 白河县| 娄底市| 合川市| 南宁市| 益阳市| 内黄县| 无为县| 巢湖市| 盘山县| 张家口市| 义乌市| 金华市| 瓮安县| 建阳市| 乌鲁木齐市| 武清区| 蒙山县| 进贤县| 荔波县| 岢岚县| 夏津县| 临泽县| 公主岭市| 赫章县| 剑阁县| 祥云县| 平谷区| 邢台县| 辽阳县|