1. 程式人生 > >Spring Security基於表示式的訪問控制

Spring Security基於表示式的訪問控制

概述

相比較以前使用配置屬性(configuration attributes)或access-decision voters,Spring Security 3.0提供了基於EL表示式的訪問控制(authorization mechanism)。該表示式可以用於web和method訪問控制(access control)。

Security提供的EL表示式root object是SecurityExpressionRoot,注意使用時,如果是role的引數,則不需要加上“ROLE_”字首,系統會自動加上,如果需要修改該字首,請使用DefaultWebSecurityExpressionHandler.

在SecurityExpressionRoot的子類WebSecurityExpressionRoot可以使用request訪問HttpServletRequest。

常見用法

針對URL訪問控制

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

除了使用SecurityExpressionRoot內部定義的方法,也可以自己定義,比如:

public class
WebSecurity {
public boolean check(Authentication authentication, HttpServletRequest request) { ... } }
<http>
    <intercept‐url pattern="/user/{userId}/**" access="@webSecurity.checkUserId(authentication,#userId)"/>
</http>

或者

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

針對Method訪問控制

針對方法級別的訪問控制比較複雜,Spring Security提供了四種註解,分別是@PreAuthorize , @PreFilter , @PostAuthorize 和 @PostFilter,如果需要使用該註解,還需要進行配置,如下:

<globalmethodsecurity prepostannotations="enabled"/>
  • @PreAuthorize

    用於判斷使用者是否有許可權使用,如下所示,只有“ROLE_USER”的使用者才有許可權呼叫。

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

    如果EL表示式需要使用method的入參,則可以使用#標註(其他請檢視DefaultSecurityParameterNameDiscoverer),例如:

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

    在JDK8以前,介面的引數名稱會丟失,此時採用上面的方法將會無法工作,這是需要使用@P註解或@Param(使用AnnotationParameterNameDiscoverer),例如:

    @PreAuthorize("#c.name == authentication.name")
    public void doSomething(@P("c") Contact contact);
    
    @PreAuthorize("#n == authentication.name")
    Contact findContactByName(@Param("n") String name);

    在JDK8,可以使用-parameters引數,以使得編譯時帶上source,獲取介面引數名稱。而 Spring 4+ 支援這種。

  • @PostAuthorize

    用於在方法呼叫後執行授權,可在表示式中使用 returnObject獲取返回值。

  • @PreFilter和@PostFilter

    兩者用於過濾collections and arrays,對滿足條件的item進行刪除,可以使用filterObject訪問輸入輸出物件。Spring Security會遍歷這個容器,並對每一個item執行el表示式,如果結果為false,則刪除。對於輸入引數,如果有多個collection,需要通過屬性filterTarget指定。

    注意對資料量特別大的引數,不建議使用,因為效率不高。為了簡化訪問控制寫法,可以自定義annotation,例如:

    @Retention(RetentionPolicy.RUNTIME)
    @PreAuthorize("#contact.name == authentication.name")
    public @interface ContactPermission {}