前言
這篇是很久之前學習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"匹配。