概述
相比较以前使用配置属性(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 {}