Spring Security - @PreFilter e @PostFilter

Spring Security - @PreFilter e @PostFilter

1. Visão geral

Neste artigo, aprenderemos como usar as anotações@PreFiltere@PostFilter para proteger as operações em um aplicativo Spring.

Quando usado junto com as informações principais autenticadas,@PreFiltere@PostFilter nos permite definir regras de segurança refinadas usando Spring Expression Language.

2. Apresentando@PreFilter e@PostFilter

Simplificando, as anotações@PreFiltere@PostFilter sãoused to filter lists of objects com base nas regras de segurança personalizadas que definimos.

@PostFilter define uma regra para filtrar a lista de retorno de um método, porapplying that rule to every element in the list. Se o valor avaliado for verdadeiro, o item será mantido na lista. Caso contrário, o item será removido.

@PreFilter funciona de maneira muito semelhante, no entanto, a filtragem é aplicada a uma lista que está sendo passada como um parâmetro de entrada para o método anotado.

Ambas as anotações podem ser usadas em métodos ou tipos (classes e interfaces). Vamos usá-los apenas em métodos ao longo deste artigo.

Essas anotações não estão ativas por padrão - precisaremos habilitá-las com a anotação@EnableGlobalMethodSecurity eprePostEnabled = true - em nossa configuração de segurança:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // ...
}

3. Escrevendo regras de segurança

Para escrever as regras de segurança nessas duas anotações - faremos uso de expressões Spring-EL; também podemos usar o objeto embutidofilterObject para obter uma referência ao elemento particular da lista que está sendo testado.

Spring Security fornecemany other built-in objects para criar regras muito específicas e exatas.

For example, podemos usar@PreFilter para verificar se a propriedadeassignee de um objetoTask é igual aname do usuário autenticado atualmente:

@PostFilter("filterObject.assignee == authentication.name")
List findAll() {
    ...
}

Usamos a anotação@PostFilter aqui, pois queremos que o método execute e obtenha todas as tarefas primeiro, e eles passam cada tarefa da lista por meio de nossa regra de filtro.

Portanto, se o usuário autenticado formichael, a lista final de tarefas retornada pelo métodofindAll conteria apenas as tarefas atribuídas amichael, mesmo se o banco de dados tiver tarefas atribuídas a jim epam.

Agora vamos tornar a regra um pouco mais interessante. Suponha que, se um usuário for um gerente, ele possa ver todas as tarefas, independentemente de a quem está atribuído:

@PostFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
List findAll() {
    // ...
}

Usamos o método integradohasRole para verificar se o usuário autenticado tem a função de MANAGER. SehasRole retornar verdadeiro, a tarefa será mantida na lista final. Portanto, se o usuário for um gerente, a regra retornará verdadeiro para todos os itens da lista. Assim, a lista final conterá todos os itens.

Agora vamos filtrar uma lista passada como parâmetro para um métodosave usando@PreFilter:

@PreFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
Iterable save(Iterable entities) {
    // ...
}

A regra de segurança é a mesma que usamos no exemplo@PostFilter. A principal diferença aqui é que os itens da lista serão filtrados antes da execução do método, permitindo remover alguns itens da lista, impedindo que sejam salvos no banco de dados.

Portanto,jim, que não é gerente, pode tentar salvar uma lista de tarefas, algumas das quais atribuídas apam. No entanto, apenas as tarefas atribuídas ajim serão incluídas, as outras serão ignoradas.

4. Desempenho em listas grandes

@PreFilter é muito legal e fácil de usar, mas pode ser ineficiente ao lidar com listas muito grandes, pois a operação de busca irá recuperar todos os dados e aplicar o filtro posteriormente.

Imagine, por exemplo, que temos milhares de tarefas em nosso banco de dados e queremos recuperar as cinco tarefas que estão atualmente atribuídas apam. Se usarmos@PreFilter,, a operação do banco de dados irá buscar todas as tarefas primeiro e iterar por todas elas para filtrar aquelas que não estão atribuídas apam.

5. Conclusão

Este artigo rápido explicou como criar um aplicativo simples, mas seguro, usando as anotações@PreFilter e@PostFilter do Spring Security.

Verifique o exemplo de código completoin this Github repository.