skip to content

@PreFilter / @PostFilter

@PreFilter and @PostFilter remove elements a user may not see from collection arguments and return values, using filterObject in the expression. Interviewers point out that filtering after a database query is a performance trap, and want you to see it too.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What do @PreFilter and @PostFilter do in Spring Security, and what does filterObject refer to?

level: juniorimportance: should knowfreq 35%

answer

  1. filterObject = each element, not the collection
  2. @PreFilter -> argument, before body
  3. @PostFilter -> return value, after body
  4. keep on true, drop on false (silent)
  5. @EnableMethodSecurity enables it

basics

~10 s

They remove elements from a collection based on a security rule. @PreFilter filters a collection method argument before the method runs; @PostFilter filters the collection the method returns. filterObject is each element being tested.

solid answer

~40 s

@PreFilter and @PostFilter are method-security annotations (enabled by @EnableMethodSecurity) that filter collections against a SpEL boolean expression. @PreFilter runs before the method body and strips disallowed elements from an incoming collection argument, so the method only sees permitted items. @PostFilter runs after the method returns and strips disallowed elements from the returned collection, so the caller only receives permitted items. Inside both expressions, filterObject is bound to each individual element in turn; the element is kept when the expression evaluates to true and dropped when it is false. A typical rule is @PostFilter("filterObject.owner == authentication.name"), which returns only the rows the current user owns. This is different from @PreAuthorize/@PostAuthorize, which make a single allow/deny decision for the whole call rather than pruning a collection element by element.

code

java · 20 lines
java
@Service
public class DocumentService {

    private final DocumentRepository repository;
    public DocumentService(DocumentRepository repository) { this.repository = repository; }

    // AFTER the method returns, keep only documents the current user owns.
    // filterObject is each Document in the returned list.
    @PostFilter("filterObject.owner == authentication.name")
    public List<Document> findAll() {
        return repository.findAll(); // must return a MUTABLE list
    }

    // BEFORE the body runs, drop any id the caller is not allowed to submit.
    // Inside the body, 'ids' has already been pruned.
    @PreFilter("filterObject != 'forbidden'")
    public void process(List<String> ids) {
        ids.forEach(this::doWork);
    }
}

go deeper

for a junior

Know the one-line difference: @PreFilter prunes an incoming collection argument, @PostFilter prunes the returned collection, filterObject is each element.

for a middle

Be able to write a correct SpEL rule using filterObject and authentication.name and explain that failures drop elements rather than throw.

for a senior

Contrast with @PreAuthorize/@PostAuthorize semantics and know when element-level filtering is the right tool versus a call-level decision.

for a principal

Frame filtering as a defense-in-depth convenience and discuss where authorization should really live (query vs in-memory).

## What method security is Spring Security can enforce authorization not only at the HTTP layer but directly on Spring bean methods. You turn this on with `@EnableMethodSecurity` on a `@Configuration` class (in older versions `@EnableGlobalMethodSecurity(prePostEnabled = true)`). Spring then wraps annotated beans in an AOP proxy that consults an authorization interceptor around each call. ## @PreAuthorize vs @PreFilter (the key distinction) `@PreAuthorize`/`@PostAuthorize` answer one yes/no question for the whole invocation — if the answer is no, an `AccessDeniedException` is thrown. `@PreFilter`/`@PostFilter` are different: they never throw for a failed element; instead they **silently remove** elements from a collection that fail the rule. - **@PreFilter** operates on a **collection argument** of the method, *before* the method body runs. Elements that fail the expression are removed, so your method body only ever sees the allowed elements. - **@PostFilter** operates on the **return value** of the method, *after* it runs. Elements that fail the expression are removed before the value reaches the caller. ## filterObject Inside a `@PreFilter` or `@PostFilter` SpEL expression, the special variable **`filterObject`** is bound to **each element of the collection, one at a time**. The expression must evaluate to a boolean: `true` keeps the element, `false` drops it. `filterObject` is *only* available in these two annotations — it does not exist in `@PreAuthorize`/`@PostAuthorize` (those use `returnObject`, method parameters, etc.). Common SpEL you can reference alongside `filterObject`: `authentication` (the current `Authentication`), `authentication.name`, `hasRole('X')`, `hasAuthority('X')`, and any bean method via `@beanName.method(filterObject)`. ## Example semantics `@PostFilter("filterObject.owner == authentication.name")` on `List<Document> findAll()` returns the full list from the repository, then Spring iterates it and keeps only documents whose `owner` equals the logged-in username. ## When to use Use filtering when a method returns/receives a collection and each element carries its own ownership/visibility rule. Use `@PreAuthorize` instead when the decision is about the *call as a whole* (e.g., "only admins may call this"). ## Gotchas to remember - `filterObject` is each element, **not** the whole collection. - `@PreFilter` filters an **argument**; `@PostFilter` filters the **return value** — mixing these up is the most common mistake. - Filtering silently shrinks results; it does not raise `AccessDeniedException`.

  • Can you use filterObject inside @PreAuthorize?
    No. filterObject exists only in @PreFilter/@PostFilter, where it is bound to each collection element. @PreAuthorize evaluates once for the whole call and has access to method parameters (e.g., #id) and authentication, but not filterObject.
  • What happens to an element that fails the @PostFilter expression?
    It is silently removed from the returned collection. No exception is thrown — unlike @PostAuthorize, which throws AccessDeniedException when its single check fails.

saying these in an interview costs you the question

  • Saying filterObject is the entire collection (it is one element at a time).
  • Claiming @PreFilter filters the return value (that is @PostFilter).
  • Thinking a failed element throws AccessDeniedException (filtering just drops it silently).

context

open as a page

What requirement does @PreFilter place on the collection it modifies, and how do you tell it which argument to filter when a method has several collection parameters?

level: middleimportance: should knowfreq 30%

basics

~20 s

@PreFilter removes elements in place, so the collection argument must be mutable — an immutable one (like List.of(...)) throws UnsupportedOperationException. When a method has multiple collection parameters, name the one to filter with the filterTarget attribute.

open as a page

Why can @PostFilter be dangerous for performance and pagination, and how should you handle authorization for large or paged result sets instead?

level: seniorimportance: should knowfreq 25%

basics

~20 s

@PostFilter loads every row first, then discards the forbidden ones in memory — wasteful for big datasets and it breaks pagination (a page of 20 can shrink to fewer, and totals are wrong). Better: filter in the database query itself.

open as a page

How does @PostFilter differ from @PostAuthorize, and which collection types can @PreFilter/@PostFilter actually operate on?

level: seniorimportance: should knowfreq 28%

basics

~10 s

@PostAuthorize makes one keep-or-throw decision on the whole return value (via returnObject); @PostFilter prunes a returned collection element by element (via filterObject), never throwing. Filtering supports Collection, arrays, and Stream — not Map.

open as a page

As a technical lead, how would you decide when method-level @PreFilter/@PostFilter is the right authorization mechanism versus enforcing access control elsewhere, and what pitfalls would you call out in review?

level: principalimportance: nice to knowfreq 15%

basics

~20 s

Use @PreFilter/@PostFilter only for small, per-element visibility rules on service methods. Prefer data-layer filtering for large or paged reads, keep authorization out of the persistence path for correctness, and watch for immutable-collection breakage, silent result-shrinking, and proxy/self-invocation gaps.

open as a page