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,如果需要使用該註解,還需要進行配置,如下:
<global‐method‐security pre‐post‐annotations="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 {}