【詳解】Spring Security的GrantedAuthority(已授予的權限)

轉自: 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"匹配。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章