What do @PreAuthorize and @PostAuthorize do, and how do you turn them on in a Spring application?
answer
- Pre = before, Post = after
- @EnableMethodSecurity (SS6) replaces @EnableGlobalMethodSecurity
- SpEL boolean -> AccessDeniedException on false
- PostAuthorize sees returnObject
- 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@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
Know Pre = before, Post = after, and that you must add @EnableMethodSecurity to switch them on.
Explain that they take SpEL, throw AccessDeniedException, and that @PostAuthorize can inspect the returned object.
Discuss @EnableMethodSecurity vs the deprecated @EnableGlobalMethodSecurity and that enforcement is proxy/AOP-based.
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'