前言
這篇是很久之前學習Spring Security整理的部落格,發現瀏覽量都1000多了,一個贊都沒有,那說明寫得確實不怎麼樣,哈哈。應該很多初學者對這個介面存在疑問,特別是如果學習這個框架之前還了解過Shiro,可能會因為這兩個框架角色、許可權的表示方式,產生困惑。現在重新整理一下。
GrantedAuthority介面
我們知道UserDeitails介面裡面有一個getAuthorities()方法。這個方法將返回此使用者的所擁有的許可權。這個集合將用於使用者的訪問控制,也就是Authorization。
所謂許可權,就是一個字串。一般不會重複。
所謂許可權檢查,就是檢視使用者許可權列表中是否含有匹配的字串。
package org.springframework.security.core; import java.io.Serializable; public interface GrantedAuthority extends Serializable { String getAuthority(); }
"角色"如何表示?與Shiro有何不同?
在Security中,角色和許可權共用GrantedAuthority介面,唯一的不同角色就是多了個字首"ROLE_",而且它沒有Shiro的那種從屬關係,即一個角色包含哪些許可權等等。在Security看來角色和許可權時一樣的,它認證的時候,把所有許可權(角色、許可權)都取出來,而不是分開驗證。
所以,在Security提供的UserDetailsService預設實現JdbcDaoImpl中,角色和許可權都儲存在auhtorities表中。而不是像Shiro那樣,角色有個roles表,許可權有個permissions表。以及相關的管理表等等。
GrantedAuthority介面的預設實現SimpleGrantedAuthority
package org.springframework.security.core.authority; import org.springframework.security.core.GrantedAuthority; import org.springframework.util.Assert; public final class SimpleGrantedAuthority implements GrantedAuthority { private static final long serialVersionUID = 500L; private final String role; public SimpleGrantedAuthority(String role) { Assert.hasText(role, "A granted authority textual representation is required"); this.role = role; } public String getAuthority() { return this.role; } public boolean equals(Object obj) { if (this == obj) { return true; } else { return obj instanceof SimpleGrantedAuthority ? this.role.equals(((SimpleGrantedAuthority)obj).role) : false; } } public int hashCode() { return this.role.hashCode(); } public String toString() { return this.role; } }
注意,在構建SimpleGrantedAuthority物件的時候,它沒有新增任何字首。所以表示"角色"的許可權,在資料庫中就帶有"ROLE_"字首了。所以authorities表中的檢視可能是這樣的。
角色和許可權能否分開儲存?角色能不能不帶"ROLE_"字首
當然可以分開儲存,你可以定義兩張表,一張存角色,一張存許可權。但是你自定義UserDetailsService的時候,需要保證把這兩張表的資料都取出來,放到UserDails的許可權集合中。當然你資料庫中儲存的角色也可以不帶"ROLE_"字首,就像這樣。
但是前面說到了,Security才不管你是角色,還是許可權。它只比對字串。
比如它有個表示式hasRole("ADMIN")。那它實際上查詢的是使用者許可權集合中是否存在字串"ROLE_ADMIN"。如果你從角色表中取出使用者所擁有的角色時不加上"ROLE_"字首,那驗證的時候就匹配不上了。
所以角色資訊儲存的時候可以沒有"ROLE_"字首,但是包裝成GrantedAuthority物件的時候必須要有。
許可權檢查/訪問控制方式
許可權檢查有兩種方式,一種是在配置類中,指定粗粒度的訪問控制,另一種是使用註解細粒度的控制訪問。
粗粒度訪問控制,所有URL以"/admin"開頭的使用者必須擁有角色"ADMIN"才能訪問。實際上操作的時候hasRole表示式,會判斷引數是否包含"ROLE_"字首,如果沒有則加上去,然後再去校驗。有這個字首則直接校驗。
protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").access("hasRole('ADMIN')") .antMatchers("/user/**").access("hasRole('USER')") .anyRequest().authenticated(); }
細粒度的訪問控制
注:需要使用註解@EnableGlobalMethodSecurity(prePostEnabled=true) 開啟
@PreAuthoritze("hasAuthority('readArtical')") public List<Artical> getAll() { //... }
這個註解,會從SecurityContext中取出Authencation物件,然後再取出Collection<GrantedAuthority> authorites集合。然後比對當前使用者是否有許可權"readArtical"。實際上就是比對集合中是否有那個GrantedAuthority的getAuthority()方法返回的字串與"radArtical"匹配。