Terry.Li-彬

          虛其心,可解天下之問;專其心,可治天下之學;靜其心,可悟天下之理;恒其心,可成天下之業。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            143 隨筆 :: 344 文章 :: 130 評論 :: 0 Trackbacks

          24.1.?概述

          請注意:2.0.0之前,Spring Security稱為Acegi Security。老的Acegi Security提供了一個ACL模塊,放在org.[acegisecurity/springsecurity].acl包下。這個老的現在已經被拋棄,并可能會在將來的Spring Security發行版中去掉。本章討論新的ACL模塊,官方推薦在Spring Security 2.0.0或更高版本中使用,它被置于org.springframework.security.acls包下。

          復雜的應用常常會有需要定義訪問權限——不只是在web請求和方法調用水平的控制。而是安全決議需要由“who (Authentication), where (MethodInvocation) and what (SomeDomainObject)”構成。也就是說,授權決議還要考慮方法調用中的實際域對象。

          想象你正在為寵物診所設計一個應用。你的基于Spring的應用主要有兩組用戶:寵物診所的職員和客戶。職員可以訪問所有數據,但客戶只能看到自己的客戶記錄。為了使這個例子更加有趣一點,你的客戶可以讓其它客戶看他們的客戶記錄,例如他們"puppy preschool"的顧問或本地"Pony Club"的總裁。以Spring Security為基礎,你有幾個方式可以選擇:

          1.????? 編寫一個自己的業務方法強制安全控制。你可以從Customer域對象中獲取一個有訪問權限用戶的集合??梢杂?code>SecurityContextHolder.getContext().getAuthentication()來訪問Authentication對象。

          2.????? 編寫一個 AccessDecisionVoter ,通過 Authentication 中儲存的 GrantedAuthority[] 來控制安全檢查。這意味著你的 AuthenticationManager 要把代表用戶對域對象訪問權限的 GrantedAuthority []填充到Authentication對象中去。

          3.????? 編寫一個AccessDecisionVoter來強制安全控制,直接打開目標Customer域對象。這意味著你的voter需要訪問DAO來獲取Customer對象。它接著會訪問Customer對象集合來進行安全決議。

          這些方法都是合情合理的。但是,第一個方法把你的授權檢查和業務代碼耦合到一起了。帶來的主要問題包括提高了單元測試的難度,以及Customer授權邏輯很難在其它地方重用。從Authentication中獲取GrantedAuthority[]是好的,但是面對數量很大的Customers時也是不行的。如果用戶訪問的Customers數量可能達到5,000個(在這個例子里不大可能,但是想象如果是一個很大的Pony Club里的獸醫?。?,構建Authentication需要耗費的內存和時間可能不是你想要的。最后一種方法,編寫代碼從外部直接打開Customer,可能是這3個方法里最好的一個。它成功分離了關注點,也沒有濫用內存和CPU,但還是效率很低,AccessDecisionVoter和最終的業務方法都要自己來調用DAO方法來獲取Customer對象。一次業務方法調用要訪問兩次DAO很明顯也不是你想要的。另外,上面提到的每一種方法你都要自己從頭來編寫自己的訪問控制列表(ACL)持久化和業務邏輯。

          幸運地,還有另外一個選擇,下面我們來討論一下這個選擇。

          24.2.?關鍵概念

          Spring SecurityACL服務發布為spring-security-acl-xxx.jar。要使用Spring Security的域對象安全,你要把這個包加入到你的classpath中。

          Spring Security的域對象安全以訪問控制列表(ACL)為核心概念。你系統里的每個域對象都有自己的ACL,這個ACL記錄了誰能或不能訪問該域對象。Spring Security為你的應用帶來3個主要的ACL相關能力。

          ·???????? 提供一條可以高效地為你所有的域對象獲取ACL條目(以及修改ACL)的途徑;

          ·???????? 提供一條在方法被調用前保證給定用戶有訪問你的域對象許可的途徑;

          ·???????? 提供一條在方法被調用后保證給定用戶有訪問你的域對象許可的途徑;

          正如第一點所說,Spring SecurityACL模塊的主要能力之一是提供一個高性能獲取ACL的途徑。這個ACL repository的能力是非常重要的,因為你系統中的每一個域對象可能有若干個訪問控制條目,每個ACL又可能是從其他ACL繼承而來——樹型結構(這很常用,Spring Security提供了很容易使用的支持)。Spring SecurityACL模塊已經很小心的考慮了設計以滿足高性能的ACL檢索,它利用了可插接的緩存模塊,死鎖最小化的數據庫更新,獨立于ORM框架(我們使用直接JDBC),適當的包裝,以及透明的數據庫更新。

          數據庫的設計是以ACL模塊的操作為中心的,讓我們來看看該模塊的實現里缺省使用的4個主要數據表。下面列出在一個典型的Spring Security ACL部署中的數據表,以數據量大小排序,行數最多的在最后面:

          ·???????? ACL_SID使我們能夠在系統中唯一定義任何principalauthority(“SID”表示“Security IDentity”)。主鍵是ID,SID的文字表示,以及一個表示該SIDprincipal名還是GrantedAuthority的標識。每個principalGrantedAuthority都有一個對應的記錄。當在接受許可的上下文中使用的時候,SID通常被稱為“接收者(recipient)”。

          ·???????? ACL_CLASS使我們能夠在系統中唯一定義任何域對象類。主鍵是ID,然后是CLASSjava類名)。每個我們想要為其存儲ACL許可的類有一個記錄。

          ·???????? ACL_OBJECT_IDENTITY存儲系統里每個域對象實例的信息。其中的列包括ID;一個到表ACL_CLASS 的外鍵OBJECT_ID_CLASS;一個使我們知道是為哪個ACL_CLASS類實例提供信息的唯一標識OBJECT_ID_IDENTITY;父對象,一個到ACL_SID的外鍵OWNER_SID表示該域對象的所有者;ENTRIES_INHERITING表示是否允許ACL條目從ACL父條目繼承。

          ·???????? 最后,ACL_ENTRY存儲為每個recipient分配的權限。它的列包括一個到表ACL_OBJECT_IDENTITY的外鍵,recipient(例如一個到ACL_SID的外鍵),是否需要審計,以及一個表示許可是授予還是拒絕的“整數位掩碼”(許可可能包括多個方面,如view, edit, delete)。每個recipient對每個域對象的許可都有一個記錄


          上一段中提到,ACL系統使用了“整數位掩碼”。不要擔心,你不需要知道這些數字指向的具體位移來使用ACL系統,只需要知道我們有32個開關可以打開或關閉就可以了(一個int數值4byte,共32bits)。每一個位代表一個permission,缺省地,這些permission是:讀(bit 0),寫(bit 1),創建(bit 2),刪除(bit 3)和管理(bit 4)。如果你要使用其他permission,可以很容易的實現自己的permission實例,ACL框架的其余部分不需要具備你定義的擴展的知識,即可完成操作。

          明白我們采用這種“整數位掩碼”對你的系統中域對象的數量是完全沒有影響這一點是很重要的。我們的permission只能使用32bit,但是你可以有數以十億計的域對象(這也意味著會有數以十億計的記錄在ACL_OBJECT_IDENTITY表中,ACL_ENTRY表的記錄則更多)。我們特別說明這一點是因為發現有些用戶錯誤的以為我們要為每個潛在的域對象使用一個bit,這是不對的。

          現在我們已經提供了一個ACL系統的基本概覽,以及在表結構上看起來的樣子,現在讓我們來看看關鍵接口。關鍵接口列表如下:

          ·???????? Acl:每一個域對象有且僅有一個ACL的對象,它內部擁有AccessControlEntry,并知道Acl的所有者。Acl不直接指向域對象,而是指向一個ObjectIdentity。Acl是存儲在ACL_OBJECT_IDENTITY表中的。

          ·???????? AccessControlEntry :一個 Acl 包含多個 AccessControlEntry ,在ACL框架中,我們常常把它簡稱為ACE。每個ACE都指向一組Permission,SidAclACE可以是grantingnon-granting的,同時包含有審計的相關設置。ACE保存在ACL_ENTRY表中。

          ·???????? Permission Permission 表示一個不可變的位掩碼,并為位掩碼機制和信息輸出提供方便的功能。上面提到的 5 個基本的 Permission (bits 0 4)包含在BasePermission類中。

          ·???????? Sid ACL 模塊需要參照到 principal GrantedAuthority[] 。 Sid 接口提供實際安全對象(如 principal, role, group 等等)和 Acl 中實際內容的間接聯系,簡稱“安全身份”。普通的實現包括有 PrincipalSid (在 Authentication 中表示一個 principal )和 GrantedAuthoritySid 。安全身份信息放在 ACL_SID表中。

          ·???????? ObjectIdentity ACL 模塊中,內部用 ObjectIdentity 來表示每個域對象。缺省的實現是 ObjectIdentityImpl 。

          ·???????? AclService :用于獲取 ObjectIdentity 適用的 Acl 。在包含的實現( JdbcAclService )中,獲取操作委派給 LookupStrategy LookupStrategy 為獲取 ACL 信息提供高度優化的策略,使用批獲?。?/code> BasicLookupStrategy ),也支持用戶實現以提供更好的性能,例如層次化查詢和類似的以性能為中心的非 ANSI SQL 能力。

          ·???????? MutableAclService :允許修改 Acl 以便進行持久化,如果不是為了這個可以不必使用這個接口。

          請注意我們提供的AclService和相關的數據庫類都是使用ANSI SQL的。因此可以工作在所有主流數據庫服務器上。在編寫本文的時候,已經在Hypersonic SQL, PostgreSQL, Microsoft SQL Server Oracle上測試過。

          Spring Security發行包中有兩個例子可以用來演示ACL模塊。其一是Contacts例子,另外一個是Document Management System (DMS)。我們建議你仔細看看這些例子。

          24.3.?入門

          要開始使用Spring SecurityACL能力,你需要把你的ACL保存在某個地方。這要通過Spring實例化一個DataSource。然后該數據源被注入到JdbcMutableAclServiceBasicLookupStrategy。后者提供了高性能的ACL獲取能力,前者提供了可改變的能力。請參考發行包中任何一個例子來了解如何進行配置。你還需要往上一節中提到的四個表中填充數據(參考ACL例子看相關SQL語句)。

          創建所需數據表并實例化JdbcMutableAclService后,你還要確認你的領域模型支持與Spring Security ACL包互操作。很可能ObjectIdentityImpl已經提供了足夠的支持,它提供了多種可用的方式。大部分的域對象會包含一個public Serializable getId()方法。如果返回類型是long,或者和long兼容(例如int),你就不需要煩ObjectIdentity的問題了。ACL模塊的很多部分都依賴于long identifier。如果沒有使用long(或intbyte等),你有機會要重新實現幾個類。我們不打算在Spring Security ACL模塊中支持非long identifier,因為long和所有的數據庫sequence兼容,也是最為常用的identifier數據類型,同時有足夠的長度支持通常的應用場景。

          下面的代碼展示如何創建一個Acl,或修改一個已存在的Acl

          				// Prepare the information we'd like in our access control entry (ACE)
          		
          				ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
          		
          				Sid sid = new PrincipalSid("Samantha");
          		
          				Permission p = BasePermission.ADMINISTRATION;
          		
          				// Create or update the relevant ACL
          		
          				MutableAcl acl = null;
          		
          				try {
          		
          				?acl = (MutableAcl) aclService.readAclById(oi);
          		
          				} catch (NotFoundException nfe) {
          		
          				?acl = aclService.createAcl(oi);
          		
          				}
          		
          				// Now grant some permissions via an access control entry (ACE)
          		
          				acl.insertAce(acl.getEntries().length, p, sid, true);
          		
          				aclService.updateAcl(acl);
          		

          在上面的例子中,我們獲取的ACL是和一個標識(identifer)為44"Foo"域對象聯系在一起的。我們增加一個ACE以便一個叫"Samantha"principal"administer"該對象。除了insertAce方法,這段代碼不需要再多加說明。它的第一個參數是新的ACE要插入到Acl的什么位置。上面的例子中,我們把它放到最后。最后一個參數是一個表示該ACE授權或拒絕的boolean值。大部分時候應該為授權(true),但是如果為拒絕(false),該permission會被有效的關閉。

          Spring Security沒有在DAOrepository中為自動創建、更新或刪除ACL提供任何特別集成。你要為域對象編寫像上面這樣的代碼。在你的業務層中使用AOP來自動集成ACL信息和你業務層操作是值得考慮的。我們發現這是一種很有效的方式。

          一旦你已經使用了上面的技術來在數據庫中存儲一些 ACL 信息,下一步就是實際如何使用這些 ACL 信息作為你的授權決議邏輯的一部分。在這里你有好幾個選擇。你可以分別在方法調用前和調用后編寫自己的 AccessDecisionVoter AfterInvocationProvider 。這些類應該使用 AclService 來獲取相關 ACL ,然后調用 Acl.isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode) 來決定是授權還是拒絕。你也可以使用我們的 AclEntryVoter , AclEntryAfterInvocationProvider AclEntryAfterInvocationCollectionFilteringProvider 類。所有這些類都提供了基于聲明的方式,運行時根據這些設定信息來進行評估,把你從編寫代碼中解放出來。請參考例子應用學習如何使用這些類。
          posted on 2010-08-26 16:01 禮物 閱讀(896) 評論(0)  編輯  收藏 所屬分類: Spring Security

          只有注冊用戶登錄后才能發表評論。

          網站導航:
           
          主站蜘蛛池模板: 平乐县| 西丰县| 三河市| 商都县| 泸水县| 万载县| 甘德县| 肥西县| 永福县| 南江县| 扶风县| 东乡族自治县| 康乐县| 五寨县| 南城县| 合肥市| 黑龙江省| 滁州市| 高淳县| 哈尔滨市| 和林格尔县| 涿州市| 鄂温| 楚雄市| 永春县| 碌曲县| 浦东新区| 时尚| 鹤山市| 迁安市| 信丰县| 澄迈县| 侯马市| 泰兴市| 舞阳县| 柞水县| 巨鹿县| 北辰区| 当阳市| 章丘市| 宾阳县|