@PreAuthorize vs @Secured vs @RolesAllowed
Fast recall
Quick revision
Core answer
@authService.canAccess(#id, authentication)). `@Secured` accepts a list of role strings only and predates SpEL. `@RolesAllowed` is the JSR-250 standard, also role-string only. For any non-trivial logic — ownership, parameter checks, custom evaluators — `@PreAuthorize` is the modern default. Combine with `@PostAuthorize` for return-value checks and `@PreFilter`/`@PostFilter` for collection element filtering. Enable with `@EnableMethodSecurity` in Spring Security 6 (replaces deprecated @EnableGlobalMethodSecurity).Knowledge of method security options and when to use SpEL expressions vs simple role checks.
Using @Secured or @RolesAllowed and then discovering they cannot express 'only the owner of this resource can edit it' — that requires SpEL in @PreAuthorize.
Mention @PostAuthorize for filtering return values, @PreFilter/@PostFilter for collections, and that method security requires @EnableMethodSecurity (not @EnableGlobalMethodSecurity in Spring Security 6).
Interview-ready guide
Interview answer
`@PreAuthorize` — the standard choice
@PreAuthorize("hasRole('ADMIN')")— simple role check@PreAuthorize("hasRole('USER') and #id == authentication.name")—#idreferences method parameter@PreAuthorize("@authService.canAccess(#id, authentication)")— invoke a Spring bean- Evaluated before method runs
`@PostAuthorize` — for ownership checks on return values
@PostAuthorize("returnObject.ownerId == authentication.name")- Method runs first, then expression evaluates against return
- Use when you can't determine authorization without the result
`@Secured` and `@RolesAllowed`
@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})— Spring-specific; role strings only@RolesAllowed("ADMIN")— JSR-250; Java standard; role strings only- No SpEL — use
@PreAuthorizefor anything beyond a simple role list
`@PreFilter` / `@PostFilter`
@PreFilter("filterObject.userId == authentication.name")— remove input elements user doesn't own@PostFilter("filterObject.public or filterObject.userId == authentication.name")— filter results- Powerful for multi-tenant data isolation
Self-invocation trap
- AOP-based;
this.method()bypasses the proxy - Always inject the bean and call through proxy for method security to work
Enable
@EnableMethodSecurityin Spring Security 6- Replaces deprecated
@EnableGlobalMethodSecurity @PreAuthorizeenabled by default;securedEnabled=true/jsr250Enabled=trueopt-in
@PreAuthorize vs @Secured vs @RolesAllowed
Direct Answer
@PreAuthorize is the most powerful — accepts SpEL and can inspect method arguments, return values, and Spring beans (@authService.canAccess(#id, authentication)). @Secured accepts a list of role strings only and predates SpEL. @RolesAllowed is the JSR-250 standard, also role-string only. For any non-trivial logic — ownership, parameter checks, custom evaluators — @PreAuthorize is the modern default. Combine with @PostAuthorize for return-value checks and @PreFilter/@PostFilter for collection element filtering. Enable with @EnableMethodSecurity in Spring Security 6 (replaces deprecated @EnableGlobalMethodSecurity).
Code Example
All three annotations gate method calls, but they differ in expressiveness and origin.
@Configuration
@EnableMethodSecurity(
prePostEnabled = true, // enables @PreAuthorize, @PostAuthorize (Spring)
securedEnabled = true, // enables @Secured (Spring, legacy)
jsr250Enabled = true // enables @RolesAllowed (Jakarta / JSR-250)
)
public class MethodSecurityConfig {}
@Service
public class DocumentService {
// @Secured — simple, role list only, no SpEL
@Secured({"ROLE_ADMIN", "ROLE_EDITOR"})
public void publish(Long id) { ... }
// @RolesAllowed — identical semantics, JSR-250 standard, works in non-Spring EE code
@RolesAllowed({"ADMIN", "EDITOR"})
public void archive(Long id) { ... }
// @PreAuthorize — full SpEL: arguments, return value, bean methods, authentication principal
@PreAuthorize("hasRole('ADMIN') or (#doc.ownerId == authentication.name and #doc.draft)")
public void edit(@P("doc") Document doc) { ... }
@PostAuthorize("returnObject.ownerId == authentication.name or hasRole('ADMIN')")
public Document load(Long id) { return repo.findById(id).orElseThrow(); }
// Custom permission bean referenced via @acl
@PreAuthorize("@acl.canDelete(#id, authentication)")
public void delete(Long id) { ... }
}
| Annotation | Expression | Origin | Standout feature |
|---|---|---|---|
@Secured | role names only | Spring (legacy) | minimal, fast |
@RolesAllowed | role names only | Jakarta / JSR-250 | portable across EE |
@PreAuthorize | full SpEL | Spring | arguments, SpEL, beans |
@PostAuthorize | full SpEL on return | Spring | filter after the fact |
- Use
@PreAuthorizeby default — it supports argument-level checks, bean references (@acl.canDo(...)), and SpEL onauthenticationandprincipal. - Pick
@Securedor@RolesAllowedonly if your team prefers plain role lists or you target a non-Spring runtime. - Enable only the flags you use (
prePostEnabled,securedEnabled,jsr250Enabled) — each one adds an AOP advisor to every secured bean. - Remember the proxy rule: these annotations work only on public methods of Spring-managed beans, called through the proxy.
Common Mistakes & Pitfalls
Using @Secured or @RolesAllowed and then discovering they cannot express 'only the owner of this resource can edit it' — that requires SpEL in @PreAuthorize.
Check your understanding
Answer these without looking back at the article.
- How would you use @PostAuthorize to ensure a user can only read their own data?
- What is the difference between hasRole('ADMIN') and hasAuthority('ROLE_ADMIN') in SpEL?
- How does Spring Security implement method security internally — through AOP or something else?
- How do you write a custom PermissionEvaluator and use it via hasPermission() in SpEL?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.