JdbcDaoImpl的高級配置
JdbcDaoImpl擁有眾多的可配置選項使其可以在已存在的schema中使用,或對其功能進行更復雜地調整。在很多場景下,很可能我們隻需調整內置UserDetailsService類的配置而不需要寫自己的代碼。
有一個很重要的功能就是在用戶(User)和權限(GrantedAuthority)之間添加一個隔離層(a level of indirection——找不到更好的譯法瞭),這通過將GrantedAuthority按照邏輯劃分成集合即組(group)來實現。用戶能夠被分配到一個或多個組,而組的成員被賦予瞭一系列的GrantedAuthority聲明。
正如在圖中所描述的那樣,中間的隔離層使得我們可以將相同集合的角色分派給很多人,而這隻需要指定新用戶到存在的組中即可。將這與我們之前的做法對比,在以前的做法是將GrantedAuthority直接分配給單個的用戶。
這種將權限進行打包處理的方式可能在以下的場景中用到:
l 要將用戶分成不同的組,而組之間有些角色是重疊的;
l 想要全局地修改一類用戶的權限。如,如果你擁有一個“供應商”的分組,而你想要修改他們能否訪問應用特定區域的設置;
l 擁有大量的用戶,你不需要用戶級別的授權配置。
除非你的應用用戶量很有限,否則很可能要使用基於組的訪問控制。這種管理方式的簡便性和擴展性帶來的價值遠遠超過瞭它稍微增加的復雜性。這種將用戶權限集中到組中的技術通常叫做基於組的訪問控制(Group-Based Access Control ,GBAC)。
【基於組的訪問控制幾乎在市面上任何安全的操作系統和軟件包中都能看到。微軟的活動目錄(Active Directory,AD)是大范圍使用GBAC的典型實現,它把AD的用戶納入組中並給組授權權限。通過使用GBAC,能夠指數級得簡化對大量基於AD組織的權限管理。想一下你所使用軟件的安全功能——用戶、分組以及權限是如何管理的?這種方式編寫安全功能的利弊是什麼?】
讓我們對JBCP Pets添加一層抽象,並將基於組的授權理念應用於這個站點。
配置基於組的授權
我們會為站點添加兩個組——普通用戶(我們將其稱為“Users”)和管理員(我們將其稱為“Administrators”)。通過修改用於啟動數據庫的SQL腳本,將已經存在的guest和admin賬號分配到合適的組中。
配置JdbcDaoImpl以使用用戶組
首先,我們需要為JdbcDaoImpl的自定義實現類設置屬性以啟用組的功能,並關閉對用戶直接授權的功能。在dogstore-base.xml中添加如下的bean聲明:
Xml代碼
<bean id="jdbcUserService"
class="com.packtpub.springsecurity.security.CustomJdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="enableGroups" value="true"/>
<property name="enableAuthorities" value="false"/>
</bean>
註意的是,如果你一直跟著我們的例子在做,並且使用瞭JdbcUserManager的代碼和配置,請對其進行修改,因為在本章的剩餘部分我們將使用CustomJdbcDaoImpl。
修改初始載入的SQL腳本
我們需要簡單修改構建數據庫的SQL語句:
l 定義我們的組信息;
l 指定GrantedAuthority聲明到組中;
l 指定用戶到組中。
簡單起見,我們聲明一個名為test-users-groups-data.sql的新SQL腳本。
首先,添加組:
Sql代碼
insert into groups(group_name) values ('Users');
insert into groups(group_name) values ('Administrators');
接下來,指定角色到組中:
Sql代碼
insert into group_authorities(group_id, authority) select id,'ROLE_
USER' from groups where group_name='Users';
insert into group_authorities(group_id, authority) select id,'ROLE_
USER' from groups where group_name='Administrators';
insert into group_authorities(group_id, authority) select id,'ROLE_
ADMIN' from groups where group_name='Administrators';
接下來,創建用戶:
Sql代碼
insert into users(username, password, enabled) values
('admin','admin',true);
insert into users(username, password, enabled) values
('guest','guest',true);
最後,指定用戶到組中:
Sql代碼
insert into group_members(group_id, username) select id,'guest' from
groups where group_name='Users';
insert into group_members(group_id, username) select id,'admin' from
groups where group_name='Administrators';
修改嵌入式的數據庫創建聲明
我們需要更新嵌入式HSQL數據庫的創建配置指向這個腳本,而不是已經存在的test-data.sql腳本:
Xml代碼
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:security-schema.sql"/>
<jdbc:script location="classpath:test-users-groups-data.sql"/>
</jdbc:embedded-database>
要註意的是,security-schema.sql腳本已經包含瞭支持組功能的表聲明,所以我們不需要修改這個腳本瞭。
到這裡,你可以重啟JBCP Pets站點,它將與以前的表現完全一致,但是,我們在用戶和權限間添加的抽象層使得我們能夠更容易地開發開發復雜的用戶管理功能。
讓我們暫時離開JBCP Pets的場景,瞭解在這個方面上一個更為重要的配置。
使用遺留的或用戶自定義的schame實現基於數據庫的認證
通常來說,Spring Security的新用戶可能需要適配用戶、組和角色到已有的數據庫schema中。盡管遺留的數據庫並不匹配Spring Security要求的數據庫schema,但我們還是可以通過配置JdbcDaoImpl來匹配它。
假設我們擁有一個如下圖所示的遺留數據庫schema,要基於它實現Spring Security:
我們能夠很容易地修改JdbcDaoImpl的配置來使用這個schema並重寫我們在JBCP Pets中使用的默認Spring Security表定義和列。
確定正確的JDBC SQL查詢
JdbcDaoImpl有三個SQL查詢,它們有定義良好的參數和返回列的集合。我們必須機遇它們提供的功能,確定每個查詢的SQL。JdbcDaoImpl的每個SQL查詢都是使用登錄時提供的用戶名作為唯一的參數。
查詢名 |
描述 |
期望得到的SQL列 |
usersByUsernameQuery |
返回匹配用戶名的一個或更多的用戶。隻有返回的第一個用戶被使用。 |
Username (string) Password (string) Enabled (Boolean) |
authoritiesByUsernameQuery |
返回用戶被直接授予的權限。一般在GBAC禁用時,被使用。 |
Username (string) Granted Authority (string) |
groupAuthoritiesByUsernameQuery |
返回用戶作為組成員被授予的權限和組的詳細信息。在GBAC功能啟用時,被使用。 |
Group Primary Key (any) Group Name (any) Granted Authority (string) |
要註意的是,在一些場景中返回的列在默認的JdbcDaoImpl實現中並沒有用到,但我們依舊需要將這些值返回。在進入下一章節前,請花費一點時間嘗試寫一下基於前面數據庫圖表中的查詢語句。
配置JdbcDaoImpl來使用自定義的SQL查詢
給不規范的數據庫使用自定義SQL查詢,我們需要在Spring Bean的配置文件中修改JdbcDaoImpl的屬性。要註意的一點是,為瞭給JdbcDaoImpl配置JDBC查詢,我們不能使用<jdbc-user-service>聲明。必要要明確實例化這個bean,如同我們在自定義JdbcDaoImpl實現時所作的那樣:
Xml代碼
<bean id="jdbcUserService"
class="com.packtpub.springsecurity.security.CustomJdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="enableGroups" value="true"/>
<property name="enableAuthorities" value="false"/>
<property name="usersByUsernameQuery">
<value>SELECT LOGIN, PASSWORD,
1 FROM USER_INFO WHERE LOGIN = ?
</value>
</property>
<property name="groupAuthoritiesByUsernameQuery">
<value>SELECT G.GROUP_ID, G.GROUP_NAME, P.NAME
FROM USER_INFO U
JOIN USER_GROUP UG on U.USER_INFO_ID = UG.USER_INFO_ID
JOIN GROUP G ON UG.GROUP_ID = G.GROUP_ID
JOIN GROUP_PERMISSION GP ON G.GROUP_ID = GP.GROUP_ID
JOIN PERMISSION P ON GP.PERMISSION_ID = P.PERMISSION_ID
WHERE U.LOGIN = ?
</value>
</property>
</bean>
這是Spring Security從已存在且不符合默認schema的數據庫中讀取設置時,唯一需要配置的地方。需要記住的是,在使用已存在的schema時,通常會需要擴展JdbcDaoImpl以支持修改密碼、重命名用戶賬號以及其他的用戶管理功能。
如果你使用JdbcUserDetailsManager來完成用戶管理的任務,這個類使用瞭大約20個可配置的SQL查詢。請參考Javadoc或源碼來瞭解JdbcUserDetailsManager使用的默認查詢。