【Spring Security】 基于表达式的访问控制
【Spring Security】 基于表达式的访问控制
Metadata
title: 【Spring Security】 基于表达式的访问控制
date: 2023-02-02 14:15
tags:
- 行动阶段/完成
- 主题场景/组件
- 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
- 细化主题/Module/SpringSecurity
categories:
- SpringSecurity
keywords:
- SpringSecurity
description: 【Spring Security】 基于表达式的访问控制
【Spring Security】 基于表达式的访问控制
Spring Security 3.0 引入了使用 Spring EL 表达式作为授权机制的能力。
内置表达式
表达式 | 说明 |
---|---|
hasRole(String role) | 返回 true 当前主体是否具有指定的角色。例如, hasRole(‘admin’)默认情况下,如果提供的角色不以 “ROLE_” 开头,则会添加它。这可以通过修改 defaultRolePrefixon 来定制 DefaultWebSecurityExpressionHandler。 |
hasAnyRole(String… roles) | 返回 true 当前主体是否具有任何提供的角色(以逗号分隔的字符串列表形式给出)。例如, hasAnyRole(‘admin’, ‘user’) |
hasAuthority(String authority) | 返回 true 当前主体是否具有指定的权限。例如, hasAuthority(‘read’) |
hasAnyAuthority(String… authorities) | 返回 true 当前主体是否具有任何提供的权限(以逗号分隔的字符串列表形式给出)例如, hasAnyAuthority(‘read’, ‘write’) |
principal | 允许直接访问代表当前用户的主体对象 |
authentication | 允许直接访问 Authentication 从 SecurityContext |
permitAll | 总是评估为 tru |
denyAll | 总是评估为 false |
isAnonymous() | 如果当前主体是匿名用户,则返回 true |
isRememberMe() | true 如果当前主体是记得,我的用户, 则返回 true |
isAuthenticated() | 如果用户不是匿名的则返回 true |
iisFullyAuthenticated() | 如果用户不是匿名或记得,我的用户, 则返回 true |
hasPermission(Object target, Object permission) | 返回 true 用户是否有权访问给定权限的提供目标。例如,hasPermission(domainObject, ‘read’) |
hasPermission(Object targetId, String targetType, Object permission) | 返回 true 用户是否有权访问给定权限的提供目标。例如,hasPermission(1, ‘com.example.domain.Message’, ‘read’) |
表达式注解
pringsecurity3.0 引入了一些新的注释,以便全面支持表达式的使用。
有四个注释支持表达式属性,以允许调用前和调用后授权检查,并支持过滤提交的集合参数或返回值。它们是 @PreAuthorize,@PreFilter,@PostAuthorize 和 @PostFilter。
相关注解
@EnableGlobalMethodSecurity
要开启注解授权,还需要开启 EnableGlobalMethodSecurity 注解。
EnableGlobalMethodSecurity 源码:
// 开启全局方法级别权限控制,类似于XML :global-method-security
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ GlobalMethodSecuritySelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableGlobalMethodSecurity {
// 开启 @PreAuthorize 和 @PostAuthorize注解支持,默认false
boolean prePostEnabled() default false;
// 确定是否应启用 Spring Security 的 {@link @Secured}注解
boolean securedEnabled() default false;
// 确定是否应启用 JSR-250 注解。默认为false。
boolean jsr250Enabled() default false;
// 指示是否要创建基于子类 (CGLIB) 的代理,而不是基于标准 Java 接口的代理默认值为 {@code false}
boolean proxyTargetClass() default false;
// 建议模式
AdviceMode mode() default AdviceMode.PROXY;
// 指示在特定连接点应用多个建议时安全顾问的执行顺序
int order() default Ordered.LOWEST_PRECEDENCE;
}
我们在 MyWebSecurityConfiguration 加上此注解:
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Secured
@Secured 用于判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀 “ROLE_“。
我们在 controller 方法上添加 @Secured 注解,并添加多个角色值,表示当前登陆用户必须有其中一个角色,否则无法访问。
@GetMapping("/test")
@Secured({"ROLE_root","ROLE_manager"})
public Object test() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
MyUser principal = (MyUser)authentication.getPrincipal();
return principal;
}
@PreAuthorize 和 @PostAuthorize
@PreAuthorize 和 @PostAuthorize 权限注解,可以作用于方法或类,可以结合 EL 表达式进行访问控制,区别是 @PreAuthorize 是方法执行前,@PostAuthorize 是执行后,当表达式结果为 true 时,才能进入。
常用表达式示例:
// 表示有ROLE_ROOT角色才能访问
@PreAuthorize("hasRole('ROLE_ROOT')")
// 表示有ROLE_root或者ROLE_manager角色即可访问
@PreAuthorize("hasAnyRole('ROLE_root','ROLE_manager')")
// 表示有add:user这个权限值即可访问,不区分角色或者权限
@PreAuthorize("hasAuthority('add:user')")
// 有其中一个权限值即可访问
@PreAuthorize("hasAnyAuthority('add:user','user:update')")
// 表示只要登录都可以访问
@PreAuthorize("permitAll()")
// 拒绝所有访问
@PreAuthorize("denyAll()")
@PreFilter 和 @PostFilter
使用 @PreFilter 和 @PostFilter 可以对集合类型的参数或返回值进行过滤。
使用 @PostFilter 注解时,Spring Security 会遍历返回的集合或映射,并删除提供的表达式为 false 的任何元素。使用 @PreFilter,尽管这是一个不太常见的要求。语法是一样的,但是如果有多个参数是集合类型,那么您必须使用 filterTarget 此注释的属性按名称选择一个。
也就是可以过滤参数或者返回值,但是一般也不会这么做,效率比较低。
比如以下案例,会过滤掉返回值中 userName 不为 test 的元素。
@PostFilter(value = "filterObject.userName == 'test'")
public Object test() {
List<User> userList = userService.list();
return userList;
}
所以最后查询,虽然数据库查询了所有数据,但是只输出了 userName 为 test 的单条数据。