轉自: https://www.cnblogs.com/longfurcat/p/9417422.html
感謝大佬
前言
這篇是很久之前學習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"匹配。