【詳解】GrantedAuthority(已授予的許可權)

貓毛·波拿巴發表於2018-08-04

前言

  這篇是很久之前學習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;
    }
}
View Code

注意,在構建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"匹配。

 
 

相關文章