Перейти к основному содержимому

Spring Security — @PreFilter и @PostFilter

· 4 мин. чтения

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 .