1. Обзор
В этой статье мы узнаем, как использовать аннотации @PreFilter
и @PostFilter
для защиты операций в приложении Spring.
При использовании вместе с аутентифицированной основной информацией @PreFilter
и @PostFilter
позволяют нам определять подробные правила безопасности с использованием Spring Expression Language.
2. Представляем @PreFilter
и @PostFilter
Проще говоря, аннотации @PreFilter
и @PostFilter
используются для фильтрации списков объектов на основе определяемых нами пользовательских правил безопасности.
@PostFilter
определяет правило для фильтрации списка возврата метода, применяя это правило к каждому элементу в списке . Если оцененное значение истинно, элемент будет сохранен в списке. В противном случае элемент будет удален.
@PreFilter
работает очень похожим образом, однако фильтрация применяется к списку, который передается в качестве входного параметра аннотированному методу.
Обе аннотации можно использовать для методов или типов (классов и интерфейсов). В этой статье мы будем использовать их только в методах.
Эти аннотации не активны по умолчанию — нам нужно включить их с помощью аннотации @EnableGlobalMethodSecurity
и prePostEnabled = true
— в нашей конфигурации безопасности:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
3. Написание правил безопасности
Чтобы написать правила безопасности в этих двух аннотациях, мы будем использовать выражения Spring-EL; мы также можем использовать встроенный объект filterObject
, чтобы получить ссылку на конкретный тестируемый элемент списка.
Spring Security предоставляет множество других встроенных объектов для создания очень конкретных и точных правил.
Например , мы можем использовать @PreFilter
, чтобы проверить, равно ли свойство assignee
объекта Task
имени
текущего аутентифицированного пользователя:
@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, задача останется в финальном списке. Таким образом, если пользователь является менеджером, правило будет возвращать значение true для каждого элемента в списке. Таким образом, окончательный список будет содержать все элементы.
Теперь давайте отфильтруем список, переданный в качестве параметра методу сохранения
, используя @PreFilter
:
@PreFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
Iterable<Task> save(Iterable<Task> entities) {
// ...
}
Правило безопасности такое же, как и в примере с @PostFilter
. Основное отличие здесь заключается в том, что элементы списка будут отфильтрованы до выполнения метода, что позволит нам удалить некоторые элементы из списка, предотвратив их сохранение в базе данных.
Таким образом , jim
, не являющийся менеджером, может попытаться сохранить список задач, некоторые из которых назначены pam
. Однако будут включены только те задачи, которые назначены Джиму
, остальные будут проигнорированы.
4. Производительность в больших списках
@PreFilter
действительно классный и простой в использовании, но он может быть неэффективным при работе с очень большими списками, поскольку операция выборки будет извлекать все данные, а затем применять фильтр.
Представьте, например, что у нас есть тысячи задач в нашей базе данных, и мы хотим получить пять задач, которые в настоящее время назначены pam
. Если мы используем @PreFilter
,
операция базы данных сначала извлечет все задачи, а затем переберет их все, чтобы отфильтровать те, которые не назначены pam
.
5. Вывод
В этой быстрой статье объясняется, как создать простое, но безопасное приложение с использованием аннотаций Spring Security @PreFilter
и @PostFilter
.
Проверьте полный пример кода в этом репозитории Github .