new無语 转载请注明原创出处,谢谢!
本章专讲基于表达式的访问控制。
内置表达式:
表达式 | 备注 |
---|---|
hasRole([role]) |
如果有当前角色,则返回true(会自动添加ROLE_ 前缀) |
hasAnyRole([role1,role2]) |
如果有任一角色即可通过校验,返回true(会自动添加ROLE_ 前缀) |
hasAuthority([authority]) |
如果有指定权限,则返回true |
hasAnyAuthority([authority1,authority2]) |
如果有指定任一权限,则返回true |
principal |
获取当前用户的主体对象 |
authentication |
获取当前用户Authentication对象,身份验证主体 |
permitAll |
总是返回true,表示全部允许 |
denyAll |
总是返回false,表示全部拒绝 |
isAnonymous() |
如果为匿名验证,则返回true |
isRememberMe() |
如果为Remember-Me自动认证,则返回true |
isAuthenticated() |
如果用户不是匿名登陆,则返回true |
isFullyAuthenticated() |
如果用户不是匿名登陆或Remember-Me自动认证则返回true |
hasPermission(Object target, Object permission) |
|
hasPermission(Object targetId, String targetType, Object permission) |
hasIpAddress
该表达式hasIpAddress
是特定于Web安全性的附加内置表达式。它是由WebSecurityExpressionRoot
类定义的。
这个表达式作用为,该URL域下,受到IP访问控制。这里0:0:0:0:0:0:0:1
为本机。
.antMatchers("/db/**").access("hasIpAddress('0:0:0:0:0:0:0:1')")
在WEB安全表达式中使用自定义Bean
如果你想扩展自定义表达式,在这里可以很轻易的引用你自己的Bean。例如下面WebSecurity
。
public class WebSecurity {
public boolean check(Authentication authentication, HttpServletRequest request) {
...
}
}
使用方法:
http
.authorizeRequests()
.antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
...
安全表达式中的路径变量
考虑REST风格的URL的应用,可以提取URL中的变量信息。
例如
public class WebSecurity {
public boolean checkUserId(Authentication authentication, int id) {
...
}
}
http
.authorizeRequests()
.antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
...
安全表达式注释
前面也说到了,表达式的注释有四种,分别是@PreAuthorize
,@PreFilter
,@PostAuthorize
和@PostFilter
。启动方式前面也有提过,这里就不说了。
最常用的注释就是@PreAuthorize
,它来决定此次请求是否可以调用当前方法。
@PreAuthorize("hasRole('ADMIN')")
public void create(Contact contact);
@PreAuthorize("#username == principal")
@RequestMapping("test11")
public String test11(String username) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return "参数名与认证用户名一致。" + principal;
}
这里大家看到,第二个代码块中的方法可以使用Spring-EL
(在表达式中可以使用任何Spring-EL
功能)进行获取参数进行校验方法调用权限。这里是参数不为当前认证用户名的情况下,不能进行请求调用方法。
- 处理参数还有一种方式,就是Spring Security的
@P
注释,如果@P
注释出现在方法的单个参数上,则会使用该值。它的好处是,在使用之后,可以不包含任何参数名称的信息。
import org.springframework.security.access.method.P;
...
@PreAuthorize("#c.name == authentication.name")
public void doSomething(@P("c") Contact contact);
- 如果Spring Data 的
@Param
的注解在方法参数上使用,也会使用该参数。
import org.springframework.data.repository.query.Param;
...
@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);
如果你有需求需要对方法进行后置校验,你就可以使用@PostAuthorize
,它可以通过内置表达式returnObject
进行返回值校验。
@PostAuthorize("returnObject == 'test1'")
@RequestMapping("test12")
public String test12(String param) {
return param;
}
使用@PreFilter
和@PostFilter
进行过滤
Spring Security还支持对集合和数组的过滤。
当使用@PostFilter
注释时,Spring Security会遍历返回的集合并删除提供的表达式为false
的所有元素。
filterObject
指的是返回集合中的当前对象。
@PostFilter("filterObject.username == 'testuser2'")
@RequestMapping("getAll")
public List<UserDetails> getAll() {
UserDetails details = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();
List<UserDetails> list = new ArrayList<>();
list.add(details);
return list;
}
您也可以在方法调用之前进行筛选@PreFilter
,尽管这是一个不太常见的要求。语法是一样的,但如果有多个参数是一个集合类型,那么你必须使用filterTarget
这个注释的属性来选择一个名称。
请注意,过滤显然不能替代您的数据检索查询。如果你正在过滤大集合并删除很多条目,那么这可能是低效的。
网友评论