SpringSecurity(二十一):權限管理


Spring Security提供的功能主要包含兩方面:認證和授權。Spring Security支持多種不同的認證方式,但是無論開發者采用哪種認證方式,都不會影響授權功能的使用,Spring Security很好的實現了認證和授權兩大功能的解耦,這也是它受歡迎的原因之一

認證就是確認用戶身份,也就是我們常說的登錄, 授權則是根據系統提前設置好的規則,給用戶分配可以訪問某一資源的權限,用戶根據自己所具有的權限,去執行相應的操作。

從技術上來說,Spring Security提供的權限管理功能主要有兩種類型:
1.基於過濾器的權限管理
2.基於AOP的權限管理

基於過濾器的權限管理主要是用來攔截HTTP請求,攔截下來之后,根據HTTP請求地址進行權限校驗。基於AOP的權限管理則主要是用來處理方法級別的權限問題,當調用某一個方法是,通過AOP將操作攔截下來,然后判斷用戶是否具備相關的權限,如果具備,則允許方法調用,否則禁止方法調用。

核心概念

角色和權限

根據之前的介紹,我們已經知道,在Spring Security用戶登錄成功后,會將當前用戶登錄信息保存在Authentication對象中,Authentication對象有一個getAuthorities方法用來返回當前對象具備的權限信息。

getAuthorities的返回值是Collection<? extends GrantedAuthority>即集合中存放的是GrantedAuthority的子類,當需要進行權限判斷的時候就會調用該方法獲得用戶的權限。無論用戶是通過何種方式登錄的,都是通過這個方法獲得權限信息。

那么對於Collection<? extends GrantedAuthority>,我們應該理解為是用戶的角色還是用戶的權限呢?

從設計層面來講,角色和權限是兩個完全不同的東西,權限是一些具體的操作,比如讀權限,寫權限;角色是某些權限的集合,比如管理員角色(ROLE_ADMIN)

從代碼層面來講,角色和權限並沒有太大的不同,在Spring Security中,角色和權限的處理方式基本是一樣的,唯一的區別在於角色在一些地方會自動添加一個ROLE_前綴,而權限不會添加任何前綴

至於getAuthorities的返回值具體是角色還是權限,取決於我們權限系統的設計。

如果系統同時存在角色和權限,我們可以使用GrantedAuthority的實現類SimpleGrantedAuthority來表示一個權限

public class Role implements GrantedAuthority{
private String name;
private List<SimpleGrantedAuthority> allowedOperations=new ArrayList<>();
@Override
public String getAuthority{
 return name;
}

省略getter/setter方法
}

角色繼承自GrantedAuthority,一個角色對應多個權限,然后在定義用戶類的時候,將角色轉為權限即可

public class User implements UserDetails{
private List<Role> roles=new ArrayList<>();
@Override
public Collection<? extends GrantedAuthority> getAuthorities(){
List<SimpleGrantedAuthority> authorities=new ArrayList<>();
for(Role role:roles){
 authorities.addAll(role.getAllowedOperations());
}
return authorities.stream().distinct().collect(Collectors.toList());
}

省略getter/setter方法
}

角色繼承

角色繼承就是指角色存在一個上下級關系,例如ROLE_ADMIN繼承自ROLE_USER,那么ROLE_ADMIN就自動具備ROLE_USER的所有權限。

Spring Security通過RoleHierarchy類對角色繼承提供支持,我們來看下它的源碼

public interface RoleHierarchy{
Collection<? extends GrantedAuthority> getReachableGrantedAuthorities(Collection<? extends GrantedAuthority> authorities)
}

只有一個getReachableGrantedAuthorities方法,該方法返回用戶真正的權限,假設用戶定義了ROLE_ADMIN繼承ROLE_USER,ROLE_USER繼承ROLE_GUEST,那么ROLE_ADMIN角色實際具有的權限也包括ROLE_USER和ROLE_GUEST的權限。

RoleHierarchy只有一個實現類RoleHierarchyImpl,開發者一般通過RoleHierarchyImpl類定義角色的層級關系。如下面代碼表示ROLE_C繼承ROLE_D,ROLE_B繼承ROLE_C,ROLE_A繼承ROLE_B

@Bean
RoleHierarchy roleHierarchy(){
RoleHierarchyImpl hierarchy=new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_A>ROLE_B>ROLE_C>ROLE_D");
return hierarchy;
}

前置處理器與后置處理器

無論是基於過濾器的權限管理還是基於方法的權限管理,前置處理器都是重中之重,二者都會用到,而后置處理器只在基於方法的權限管理中會用到

前置處理器

要了解前置處理器,我們需要先了解投票器

投票器

在Spring Security中,投票器是由 AccessDecisionVoter 接口來規范的,我們來看下 AccessDecisionVoter 接口:

public interface AccessDecisionVoter<S> {
	int ACCESS_GRANTED = 1;
	int ACCESS_ABSTAIN = 0;
	int ACCESS_DENIED = -1;
	boolean supports(ConfigAttribute attribute);
	boolean supports(Class<?> clazz);
	int vote(Authentication authentication, S object,
			Collection<ConfigAttribute> attributes);
}

1.首先一上來定義了三個常量,從常量名字中就可以看出每個常量的含義,1 表示贊成;0 表示棄權;-1 表示拒絕。
2.兩個 supports 方法用來判斷投票器是否支持當前請求。
3.vote 則是具體的投票方法。在不同的實現類中實現。三個參數,authentication 表示當前登錄主體;object 表示受保護的安全對象,如果受保護的是URL地址,則object就是一個FilterInvocation對象,如果受保護的是一個方法,則object就是一個MethodInvocation;attributes 表示當前所訪問的接口所需要的權限集合。vote方法的返回值就是之前定義的三個常量之一

Spring Security為AccessDecisionVoter提供了不同的實現類:
RoleVoter:RoleVoter是根據登陸主體的角色進行投票,即判斷當前用戶是否具備受保護對象所需要的角色。需要注意的是,默認情況下,角色需要以ROLE_開始,否則supports方法會直接返回false
RoleHierarchyVoter:繼承自RoleVoter,投票邏輯和RoleVoter一樣,不同的是RoleHierarchyVoter支持角色的繼承,它通過RoleHierarchyImpl對象對用戶所具有的角色進行解析,獲得用戶真正可觸達的角色,而RoleVoter則直接調用 authentication.getAuthorities()方法獲得用戶的角色
WebExpressionVoter:基於URL地址進行權限控制時的投票器(支持SpEL)
Jsr250Voter:處理JSR-250權限注解的投票器,如@PermitAll @DenyAll等
AuthenticatedVoter:用來判斷當前用戶的認證形式
AbstractAclVoter:基於ACL進行權限控制時的投票器,這是一個抽象類,沒有綁定到某個具體的ACL系統
AclEntryVoter:繼承自AbstractAclVoter,基於Spring Security提供的ACL權限系統的投票器
PreInvocationAuthorizationAdviceVoter:處理@PreFilter和@PreAuthorize注解的投票器

決策器

決策器由AccessDecisionManager負責,AccessDecisionManager會同時管理多個投票器,由AccessDecisionManager調用投票器進行投票,然后根據投票結果做出相應的決策

有一個抽象實現類AbstractAccessDecisionManager,AbstractAccessDecisionManager一共有三個子類:
AffirmativeBased:有一個投票器同意了,就通過。
ConsensusBased:多數投票器同意就通過,平局的話,則看 allowIfEqualGrantedDeniedDecisions 參數的取值。
UnanimousBased 所有投票器都同意,請求才通過。

這是Spring Security提供的三個決策期,如果這三個決策器無法滿足需求,開發者也可以自定義類繼承自AbstractAccessDecisionManager實現自己的決策器

權限元數據

ConfigAttribute

在介紹投票器的時候,在具體的投票方法vote中,受保護的對象所需要的權限保存在一個Collection 集合中,集合中的對象是ConfigAttribute而不是GrantedAuthority

ConfigAttribute用來存儲與安全系統相關的配置屬性,也就是系統關於權限的配置,我們看下ConfigAttribute接口

public interface ConfigAttribute extends Serializable{
 String getAttribute();
}

只有一個getAttribute方法返回具體的權限字符串,而GrantedAuthority則是通過getAuthority方法返回用戶所具有的權限,二者的返回值都是字符串是可以比較的。

SecurityMetadataSource

SecurityMetadataSource是用來提供受保護對象所需要的權限。

public interface SecurityMetadataSource extends AopInfrastructureBean {

	Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException;
	Collection<ConfigAttribute> getAllConfigAttributes();
	boolean supports(Class<?> clazz);
}

這里只有三個方法
getAttributes:根據傳入的安全對象參數返回所需要的權限,object 表示受保護的安全對象,如果受保護的是URL地址,則object就是一個FilterInvocation對象,如果受保護的是一個方法,則object就是一個MethodInvocation
getAllConfigAttributes:返回所有的角色/權限 以便驗證是否支持,不過這個方法不是必須的,可以直接返回null
supports:返回當前的SecurityMetadataSource 是否支持受保護的對象如FilterInvocation或者MethodInvocation

直接繼承SecurityMetadataSource的接口有兩個:FilterInvocationSecurityMetadataSource和MethodSecurityMetadataSource。

在實際開發中,URL地址以及訪問它所需要的權限可能保存在數據庫中,此時我們可以自定義類實現FilterInvocationSecurityMetadataSource,然后重寫里邊的getAttributes方法,在getAttributes方法中根據當前請求的URL地址去數據庫中查詢其所需要的權限,然后將查詢結果封裝為ConfigAttibute集合即可

權限表達式

Spring Security 3.0引入了SpEL表達式進行權限配置,我們可以在請求的URL或者訪問的方法上,通過SpEL來配置需要的權限

總結

首先有一個過濾器(AbstractSecurityInterceptor),FilterSecurityIntercepto就是它的URL鑒權的子類。過濾器有一個決策器AccessDecisionManager,決策器有三個子類代表不同的決策策略,每個決策器可能有多個投票器AccessDecisionVoter(我們常用的http.authorizeRequests() 就為AccessDecisionManager配置了一個WebExpressionVoter投票器)。

從SecurityMetadataSource(FilterInvocationSecurityMetadataSource是它的一個URL鑒權的實現類)中獲得URL的需要權限的集合Collection ,用戶擁有的權限從UserDetailsService實例中獲得UserDetails實例,從UserDetails實例中的getAuthorities()方法獲得權限集合Collection

Collection 可以通過SecurityConfig.createList()方法把String 或String[] 轉化為Collection

Collection 可以通過向集合中添加 new SimpleGrantedAuthority(String str)得到


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM