1. 概述
在这篇文章中,我们将学习如何在Spring应用中使用@PreFilter
和@PostFilter
注解来保护操作。
结合已认证主体信息,@PreFilter
和@PostFilter
允许我们使用Spring Expression Language定义精细的权限规则。
2. @PreFilter
和@PostFilter
介绍
简单来说,@PreFilter
和@PostFilter
注解用于根据我们自定义的security规则过滤对象列表。
@PostFilter
为方法的返回列表定义一个过滤规则,它会将规则应用于列表中的每个元素。如果评估值为真,该项将保留在列表中;否则,将移除。
@PreFilter
的工作方式类似,但过滤是应用于作为注解方法输入参数的列表。
这两个注解可以应用于方法或类型(类和接口)。本文仅在方法上使用它们。
这些注解默认不启用,我们需要在安全配置中通过@EnableGlobalMethodSecurity
注解和prePostEnabled = true
启用它们:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
// ...
}
3. 编写安全规则
在这些两个注解中编写安全规则,我们将使用Spring-EL表达式。我们还可以使用内置对象filterObject
来引用正在测试的特定列表元素。
Spring Security提供了许多其他内置对象,以创建非常具体且精确的规则。
例如,我们可以使用@PreFilter
检查Task
对象的assignee
属性是否等于当前已认证用户的name
:
@PostFilter("filterObject.assignee == authentication.name")
List<Task> findAll() {
...
}
这里我们使用了@PostFilter
注解,因为我们希望先执行方法并获取所有任务,然后将列表中的每个任务通过我们的过滤规则进行处理。
因此,如果已认证用户是michael,findAll
方法返回的任务列表将只包含分配给michael的任务,即使数据库中有分配给jim和pam的任务。
现在让我们让规则变得更有趣。假设如果用户是经理,他们可以看到所有任务,不管任务被分配给谁:
@PostFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
List<Task> findAll() {
// ...
}
我们使用内置方法hasRole
检查已认证用户是否具有MANAGER角色。如果hasRole
返回true,任务将保留在最终列表中。所以,如果用户是经理,列表中的每个项目都会通过规则,因此最终列表将包含所有项目。
现在让我们使用@PreFilter
在save
方法的参数列表上过滤:
@PreFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
Iterable<Task> save(Iterable<Task> entities) {
// ...
}
安全规则与我们在@PostFilter
示例中使用的相同。主要区别在于,列表项将在方法执行之前进行过滤,从而允许我们从列表中删除一些项,防止它们被保存到数据库中。
因此,非经理的jim可能会尝试保存一个任务列表,其中有些任务分配给了pam。但是,只有分配给jim的任务会被包括在内,其他的将被忽略。
4. 大量数据上的性能
@PreFilter
非常方便易用,但在处理大量数据时可能会效率低下,因为检索操作会首先获取所有数据,然后在之后进行过滤。
想象一下,如果我们数据库中有数千个任务,并且我们想获取当前分配给pam的五个任务。如果我们使用@PreFilter
,数据库操作将首先获取所有任务,然后遍历所有任务,筛选出那些未分配给pam的任务。
5. 总结
这篇简短的文章解释了如何使用Spring Security的@PreFilter
和@PostFilter
注解创建一个简单但安全的应用程序。
查看完整的代码示例在这个Github仓库。