Spring Security - @PreFilter и @PostFilter
1. обзор
В этой статье мы узнаем, как использовать аннотации@PreFilter и@PostFilter для защиты операций в приложении Spring.
При использовании вместе с аутентифицированной информацией о принципале@PreFilter и@PostFilter позволяют нам определять детальные правила безопасности с помощью Spring Expression Language.
2. Вводя@PreFilter и@PostFilter
Проще говоря, аннотации@PreFilter и@PostFilter - этоused to filter lists of objects, основанные на определенных нами настраиваемых правилах безопасности.
@PostFilter определяет правило фильтрации списка возврата метода поapplying that rule to every element in the list. Если оцененное значение равно true, элемент будет сохранен в списке. В противном случае элемент будет удален.
@PreFilter работает очень похоже, однако фильтрация применяется к списку, который передается в качестве входного параметра аннотированному методу.
Обе аннотации могут использоваться для методов или типов (классов и интерфейсов). В этой статье мы будем использовать их только в методах.
Эти аннотации не активны по умолчанию - нам нужно будет включить их с помощью аннотации@EnableGlobalMethodSecurity иprePostEnabled = true - в нашей конфигурации безопасности:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
3. Написание правил безопасности
Чтобы написать правила безопасности в этих двух аннотациях, мы будем использовать выражения Spring-EL; мы также можем использовать встроенный объектfilterObject, чтобы получить ссылку на конкретный проверяемый элемент списка.
Spring Security предоставляетmany other built-in objects для создания очень конкретных и точных правил.
For example, мы можем использовать@PreFilter, чтобы проверить, совпадает ли свойствоassignee объектаTask сname текущего аутентифицированного пользователя:
@PostFilter("filterObject.assignee == authentication.name")
List findAll() {
...
}
Мы использовали здесь аннотацию@PostFilter, так как мы хотим, чтобы метод выполнялся и сначала получал все задачи, а они передают каждую задачу из списка через наше правило фильтрации.
Таким образом, если аутентифицированный пользовательmichael, окончательный список задач, возвращаемый методомfindAll, будет содержать только те задачи, которые назначеныmichael, даже если в базе данных есть задачи, назначенные для jim иpam.
А теперь давайте сделаем правило немного интереснее. Предположим, что если пользователь является менеджером, он может видеть все задачи, независимо от того, кому они назначены:
@PostFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
List findAll() {
// ...
}
Мы использовали встроенный методhasRole, чтобы проверить, имеет ли аутентифицированный пользователь роль МЕНЕДЖЕР. ЕслиhasRole вернет истину, задача останется в конечном списке. Таким образом, если пользователь является менеджером, правило вернет true для каждого элемента в списке. Таким образом, окончательный список будет содержать все элементы.
Теперь давайте отфильтруем список, переданный в качестве параметра методуsave, используя@PreFilter:
@PreFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
Iterable save(Iterable entities) {
// ...
}
Правило безопасности такое же, как и в примере@PostFilter. Основное отличие здесь состоит в том, что элементы списка будут отфильтрованы до выполнения метода, что позволит нам удалить некоторые элементы из списка, не позволяя сохранить их в базе данных.
Итак,jim, который не является менеджером, может попытаться сохранить список задач, некоторые из которых назначеныpam. Однако будут включены только те задачи, которые назначеныjim, остальные будут проигнорированы.
4. Производительность в больших списках
@PreFilter действительно крутой и простой в использовании, но он может быть неэффективным при работе с очень большими списками, поскольку операция выборки извлечет все данные и затем применит фильтр.
Представьте, например, что у нас есть тысячи задач в нашей базе данных, и мы хотим получить пять задач, которые в настоящее время назначеныpam. Если мы используем@PreFilter,, операция базы данных сначала выберет все задачи и выполнит итерацию по всем из них, чтобы отфильтровать те, которые не назначеныpam.
5. Заключение
В этой быстрой статье объясняется, как создать простое, но безопасное приложение с использованием аннотаций@PreFilter и@PostFilter Spring Security.
Посмотрите полный пример кодаin this Github repository.