Spring Security - @PreFilter и @PostFilter

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.