What do @PreFilter and @PostFilter do in Spring Security, and what does filterObject refer to?
answer
- filterObject = each element, not the collection
- @PreFilter -> argument, before body
- @PostFilter -> return value, after body
- keep on true, drop on false (silent)
- @EnableMethodSecurity enables it
basics
~10 sThey 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@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
Know the one-line difference: @PreFilter prunes an incoming collection argument, @PostFilter prunes the returned collection, filterObject is each element.
Be able to write a correct SpEL rule using filterObject and authentication.name and explain that failures drop elements rather than throw.
Contrast with @PreAuthorize/@PostAuthorize semantics and know when element-level filtering is the right tool versus a call-level decision.
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).