skip to content

@PreAuthorize / @PostAuthorize

@PreAuthorize evaluates a SpEL expression over the arguments and principal before the call, and @PostAuthorize can inspect the returned object — and both are proxy-based, so self-invocation skips them. Interviewers ask why @PostAuthorize on a bulk method is a bad idea.

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

questions

5

What do @PreAuthorize and @PostAuthorize do, and how do you turn them on in a Spring application?

level: juniorimportance: must knowfreq 78%

answer

  1. Pre = before, Post = after
  2. @EnableMethodSecurity (SS6) replaces @EnableGlobalMethodSecurity
  3. SpEL boolean -> AccessDeniedException on false
  4. PostAuthorize sees returnObject
  5. proxy-enforced service layer

basics

~10 s

@PreAuthorize checks a rule before a method runs; @PostAuthorize checks after it returns. You enable them by adding @EnableMethodSecurity to a configuration class. If the rule is false, access is denied with an exception.

solid answer

~30 s

@PreAuthorize evaluates a SpEL (Spring Expression Language) boolean before the method executes; if it is false the method never runs and an AccessDeniedException is thrown. @PostAuthorize evaluates after the method returns, typically to check the returned object, and throws AccessDeniedException if false (the return value is discarded). Both are enabled by annotating a @Configuration class with @EnableMethodSecurity (Spring Security 6+; @PreAuthorize/@PostAuthorize are on by default there). The older @EnableGlobalMethodSecurity(prePostEnabled = true) did the same in Spring Security 5. Example: @PreAuthorize("hasRole('ADMIN')"). They are usually placed on service-layer methods and are enforced through a Spring AOP proxy that wraps the bean.

code

java · 19 lines
java
@Configuration
@EnableMethodSecurity // Spring Security 6+; prePostEnabled defaults to true
public class MethodSecurityConfig {
}

@Service
public class DocumentService {

    @PreAuthorize("hasRole('ADMIN')")
    public void deleteAll() {
        // only runs if current user has ROLE_ADMIN
    }

    @PostAuthorize("returnObject.owner == authentication.name")
    public Document findById(Long id) {
        // runs fully, then result is checked against the caller
        return repository.findById(id).orElseThrow();
    }
}

go deeper

for a junior

Know Pre = before, Post = after, and that you must add @EnableMethodSecurity to switch them on.

for a middle

Explain that they take SpEL, throw AccessDeniedException, and that @PostAuthorize can inspect the returned object.

for a senior

Discuss @EnableMethodSecurity vs the deprecated @EnableGlobalMethodSecurity and that enforcement is proxy/AOP-based.

for a principal

Frame the side-effect risk of @PostAuthorize and where method security sits relative to URL-level filter security.

## What method security is **Method security** lets you attach authorization rules to individual methods instead of only to URL paths. The two most common annotations are `@PreAuthorize` and `@PostAuthorize`. ## The two annotations - **@PreAuthorize** — takes a SpEL (Spring Expression Language) string that must evaluate to a boolean. It runs *before* the target method. If it returns `false`, the method body is never invoked and Spring Security throws an `org.springframework.security.access.AccessDeniedException` (which, for an authenticated user, typically surfaces as HTTP 403). Example: `@PreAuthorize("hasRole('ADMIN')")`. - **@PostAuthorize** — also takes a SpEL boolean, but runs *after* the method returns. Its distinguishing feature is access to the return value via the `returnObject` variable, e.g. `@PostAuthorize("returnObject.owner == authentication.name")`. If the expression is false, the already-computed return value is discarded and `AccessDeniedException` is thrown. Because the method body has already executed, `@PostAuthorize` should not be used to guard methods that cause side effects you cannot afford when access is ultimately denied. ## Enabling it Annotate any `@Configuration` class with `@EnableMethodSecurity`. In Spring Security 6+ this replaces the deprecated `@EnableGlobalMethodSecurity(prePostEnabled = true)`. With `@EnableMethodSecurity`, pre/post annotations are enabled by default (the `prePostEnabled` attribute defaults to `true`), so you usually just write `@EnableMethodSecurity`. You can also toggle: - `securedEnabled` (for `@Secured`) - and `jsr250Enabled` (for `@RolesAllowed`). ## Where the checks live These annotations are enforced by a **Spring AOP proxy** around the annotated bean. Spring registers `AuthorizationManagerBeforeMethodInterceptor` (for `@PreAuthorize`) and `AuthorizationManagerAfterMethodInterceptor` (for `@PostAuthorize`) as method interceptors; they consult an `AuthorizationManager` that evaluates the SpEL. Because it is proxy-based, the checks only fire when the call comes in *through* the proxy from another bean — a caveat covered by the self-invocation gotcha. ## Common SpEL helpers - `hasRole('X')`, `hasAuthority('X')`, `hasAnyRole(...)` - `permitAll`, `denyAll`, `isAuthenticated()` - `authentication`, and `principal`. `hasRole('ADMIN')` automatically checks for the authority `ROLE_ADMIN` (the `ROLE_` prefix is added for you). ## When to use `@PreAuthorize` is the default choice for almost all method-level rules. Reach for `@PostAuthorize` only when the decision genuinely depends on the returned object and the method is safe to execute speculatively.

  • What happens if the @PreAuthorize expression evaluates to false?
    The method body is never invoked and Spring throws AccessDeniedException, which normally maps to HTTP 403 for an authenticated user (or triggers the entry point / 401 flow for an anonymous one).
  • Which annotation replaced @EnableGlobalMethodSecurity in Spring Security 6?
    @EnableMethodSecurity. It enables pre/post annotations by default and uses the newer AuthorizationManager-based interceptors instead of the legacy voter/AccessDecisionManager stack.

saying these in an interview costs you the question

  • Thinking @PostAuthorize prevents the method body from running (it runs first, then the result is checked)
  • Believing the annotations work without @EnableMethodSecurity (they are silently ignored otherwise)
  • Assuming hasRole('ADMIN') matches authority 'ADMIN' rather than 'ROLE_ADMIN'

context

open as a page

What variables can you reference in @PreAuthorize/@PostAuthorize SpEL — how do you read method arguments, the principal, and the return value?

level: middleimportance: must knowfreq 72%

basics

~20 s

You can reference method arguments by name (or #p0, #p1), the current user via authentication and principal, and — only in @PostAuthorize — the method's result via returnObject. You combine them with helpers like hasRole() and hasAuthority().

open as a page

Why can a @PreAuthorize on a method be silently bypassed when another method in the same class calls it, and how do you fix that?

level: seniorimportance: must knowfreq 68%

basics

~20 s

Method security runs through a proxy that wraps the bean. When one method calls another method on the same object with this, the call never leaves the object, so the proxy is skipped and the @PreAuthorize check doesn't run. Call through the injected bean instead, or move the method to another bean.

open as a page

In Spring Security 6, what actually enforces @PreAuthorize at runtime — walk through the interceptor and AuthorizationManager path and how it differs from the old model.

level: seniorimportance: should knowfreq 42%

basics

~20 s

A method interceptor named AuthorizationManagerBeforeMethodInterceptor wraps the bean. Before the method runs it asks an AuthorizationManager (backed by a SpEL expression handler) to decide; if the decision is a deny, it throws AccessDeniedException. This replaces the old voter/AccessDecisionManager stack from @EnableGlobalMethodSecurity.

open as a page

When would you choose @PostAuthorize over @PreAuthorize, and what are the risks of guarding a method after it has already executed?

level: principalimportance: should knowfreq 38%

basics

~20 s

Use @PostAuthorize when the decision depends on the object the method returns — for example an ownership check on the loaded entity. The risk is that the method body runs first: any side effects, expensive work, or data loading happen even when access is finally denied, and only the return value is thrown away.

open as a page