第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

訪問控制表達式

1. 前言

在實際的開發(fā)過程中,權(quán)限的分配規(guī)則往往非常復(fù)雜,如果才能實現(xiàn)快速實現(xiàn)細粒度的權(quán)限策略呢?Spring Security 已為我們找到了解決辦法。

自從 Spring Security 3.0 支持了使用 Spring 語法表達式來配置安全規(guī)則,便大大降低了安全規(guī)則實現(xiàn)的復(fù)雜度。

本節(jié),我們主要討論如何通過 Spring Security 訪問控制表達式實現(xiàn)安全規(guī)則的配置。

2.Spring 內(nèi)置表達式

Spring Security 使用 Spring EL (Spring 表達式語法)用來支持表達式配置。表達式是作為運算上下文中的根級對象被執(zhí)行的。

SecurityExpressionRoot 是支持表達式的基礎(chǔ)實現(xiàn)類,它提供了一些支持 Web 或者方法層面的安全表達式。

圖片描述

以下為其支持的表達式:

hasRole(String role)

如果當(dāng)前的用戶身份信息中,包含 role 值的角色時,該表達式返回 true。

例如判斷是否具有 admin 角色:hasRole('admin')。

需要注意的是,角色名稱在 Spring Security 內(nèi)會自動增加 ROLE_ 前綴,如果需要修改該前綴,可通過 DefaultWebSecurityExpressionHandler 對象中的 defaultRolePrefix 屬性實現(xiàn)。

hasAnyRole(String… roles)

hasRole 類似,可以同時判斷多個角色,只要包含其中一種即可,多個角色用逗號隔開。

hasAuthority(String authority)

如果當(dāng)前的身份信息中包含參數(shù)中指定權(quán)限,則返回 true。

例如:hasAuthority('read')

hasAnyAuthority(String… authorities)

如果當(dāng)前的身份信息中包含參數(shù)中指定權(quán)限之一,則返回 true。多個權(quán)限之間用逗號 , 分隔。

例如:hasAnyAuthority('read', 'write')

principal

允許當(dāng)前登錄用戶直接訪問其身份信息 principal 對象。

authentication

允許直接訪問當(dāng)前安全上下文中的認(rèn)證信息 Authentication 對象。

permitAll

永遠返回 true。

denyAll

永遠返回 false。

isAnonymous()

如果當(dāng)前用戶的身份信息為匿名用戶,則返回 true。

isRememberMe()

如果當(dāng)前用戶的身份信息是來自于「記住我」認(rèn)證用戶,則返回 true。

isAuthenticated()

如果當(dāng)前用戶的身份信息不是匿名用戶,則返回 true。

isFullyAuthenticated()

如果當(dāng)前用戶的身份信息既不是匿名用戶又不是記住我自動登錄用戶,則返回 true。

hasPermission(Object target, Object permission)

如果當(dāng)前用戶包含對指定對象的訪問權(quán)限,則返回 true。

例如:hasPermission(domainObject, 'read')

hasPermission(Object targetId, String targetType, Object permission)

如果當(dāng)前用戶包含對指定對象的訪問權(quán)限,則返回 true

例如:hasPermission(1, 'com.example.domain.Message', 'read')。

3. 內(nèi)置表達式在 Web 系統(tǒng)中的使用

3.1 表達式的應(yīng)用配置

要針對 URL 應(yīng)用表達式規(guī)則,我們需要在 <http> 對象上將 use-expressions 屬性值置為 true。Spring Security 則會將 <intercept-url> 元素中 access 屬性值解釋為表達式,并根據(jù)表達式規(guī)則返回 true 或者 false 的判定結(jié)果。

例如:

<http>
    <intercept-url pattern="/admin*"
        access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
    ...
</http>

本例中,我們將 /admin 開頭的資源定義為只有包含 admin 角色,并且來源 IP 為 192.168.1.x 網(wǎng)段的用戶才可以訪問。這里出現(xiàn)的 hasIpAddress 方法在前面內(nèi)容中沒有提到,因為它是只有 Web 系統(tǒng)才包含的,被定義在 WebSecurityExpressionRoot 類中。

3.2 使用自定義方法作為表達式

如果我們需要擴展現(xiàn)有的表達式,我們可以使用 Spring Bean 里的方法。

例如,使用自定義 Bean WebSecurity 中的 check 方法:

public class WebSecurity {
        public boolean check(Authentication authentication, HttpServletRequest request) {
                ...
        }
}

我們可以在配置文件中這樣寫:

<http>
    <intercept-url pattern="/user/**"
        access="@webSecurity.check(authentication,request)"/>
    ...
</http>

或者在 Java 中直接加入配置:

http
    .authorizeRequests(authorize -> authorize
        .antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
        ...
    )

3.3 路徑參數(shù)的使用

很多時候我們的路徑是包含參數(shù)信息的,而規(guī)則的應(yīng)用往往也和參數(shù)信息想匹配。例如針對用戶信息的 RESTful 資源地址 /user/{userId}。我們可以在表達式中使用這些參數(shù)。

例如,假設(shè)我們的擴展表達式方法如下:

public class WebSecurity {
        public boolean checkUserId(Authentication authentication, int id) {
                ...
        }
}

此方法的 id 參數(shù)需要傳入路徑中的 userId 的值,那我們在配置表達式時可用如下方式:

<http>
    <intercept-url pattern="/user/{userId}/**"
        access="@webSecurity.checkUserId(authentication,#userId)"/>
    ...
</http>

或者用 Java 的方式配置

http
    .authorizeRequests(authorize -> authorize
        .antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
        ...
    );

上述寫法的結(jié)果都是將 userId 作為參數(shù)傳入了方法之中,加入用戶訪問的 URL 地址為 /user/123/resource,則傳入的 id 參數(shù)為 123。

4. 方法安全表達式

方法安全表達式通常通過注解方式控制方法的訪問。這里主要使用了 @Pre 和 @Post 注解。

@Pre 代表執(zhí)行前,@Post 代表執(zhí)行后。在方法的安全表達式中,有四個相關(guān)的表達式注解,分別是:@PreAuthorize@PreFilter,@PostAuthorize@PostFilter。要啟用這四個標(biāo)簽要,首先要增加全局配置:

<global-method-security pre-post-annotations="enabled"/>

4.1 使用 @PreAuthorize 和 @PostAuthorize 進行訪問控制

@PreAuthorize 最主要的作用是決定一個方法能否被執(zhí)行。例如:

@PreAuthorize("hasRole('USER')")
public void create(Contact contact);

這段代碼的含義是,只有包含了 USER 角色的用戶,才被允許調(diào)用 create 方法。

對于權(quán)限,還有另一種更為具體的寫法:

@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);

這里我們指定了調(diào)用方法中的參數(shù),用來判斷當(dāng)前用戶是否對將被刪除的 Contact 對象具有 admin 角色。通過這種寫法,我們可以將目標(biāo)對象中的任意方法作為表達式的變量參數(shù)。

Spring Security 要在表達式中訪問方法參數(shù)有幾種方式:

  • Spring Security 可以使用 @P 注解,為變量設(shè)置別名。例如:
import org.springframework.security.access.method.P;

...

@PreAuthorize("#c.name == authentication.name")
public void doSomething(@P("c") Contact contact);
  • 使用 Spring 的 @Param 注解,為變量設(shè)置別名。例如:
import org.springframework.data.repository.query.Param;

...

@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);
  • 如果使用的是 JDK 8 + Spring 4+ ,不需要額外的注解,程序會自動發(fā)現(xiàn)參數(shù)名稱。

Spring Security 的表達式注解支持對象的屬性。例如,當(dāng)我們指定方法入?yún)⒅械膶傩悦Q和我們身份信息中的名稱一致時允許訪問:

@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);

這里我們用到了內(nèi)置參數(shù) authentication ,該對象保存在 Spring Security 的安全上下文中。我們也可以直接訪問到該對象的身份信息屬性。

@PostAuthorize 使用場景相對較少,它針對方法的返回進行權(quán)限過濾。

4.2 使用 @PreFilter 和 @PostFilter

@PreFilter 過濾器用于處理參數(shù)中的集合或者數(shù)組對象;@PostFilter 用于過濾返回值中集合或者數(shù)組對象。

例如:

@PreAuthorize("hasRole('USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();

5. 小結(jié)

本節(jié)討論了 Spring Security 中如何通過表達式的方式簡化權(quán)限規(guī)則配置,主要內(nèi)容有:

  • 表達式方式簡化了鑒權(quán)配置難度,增強鑒權(quán)規(guī)則的可讀性;
  • Spring 內(nèi)置了非常多的表達式,可以滿足大多數(shù)場景;
  • 表達式是對實例方法的引用,我們可以擴展自己的表達式鑒權(quán)方法;
  • @PreAuthorize 和 @PostAuthorize 分別代表在方法調(diào)用前或方法返回前執(zhí)行表達式鑒權(quán);
  • @PreFilter 和 @PostFilter 是對入?yún)⒒蛘叻祷刂档臈l件過濾。

下節(jié)我們討論一個新的概念「安全對象」。