Compare @PreAuthorize (prePostEnabled) with @Secured (securedEnabled) and @RolesAllowed (jsr250Enabled): when would you choose each?
answer
- PreAuthorize = SpEL (args, principal, custom beans)
- PostAuthorize = returnObject; Pre/PostFilter = collections
- @Secured / @RolesAllowed = plain role list, no SpEL
- JSR-250 = portable standard (@PermitAll/@DenyAll)
- hasRole adds ROLE_, hasAuthority does not
basics
~20 s@PreAuthorize takes a full SpEL expression, so it can check roles, method arguments, and custom logic — the most powerful. @Secured and @RolesAllowed only take plain role-name strings for simple checks. Prefer @PreAuthorize unless you want a standard/simple role list.
solid answer
~40 sAll three restrict who can call a method, but differ in expressiveness. @PreAuthorize (enabled by prePostEnabled, on by default) accepts a SpEL expression: hasRole/hasAuthority, boolean combinations, references to method arguments (#id), the authenticated principal, and custom bean method calls — plus @PostAuthorize can inspect the return value and @PreFilter/@PostFilter can filter collections. @Secured (securedEnabled) takes only a list of authority strings like @Secured("ROLE_ADMIN") — OR semantics, no SpEL. @RolesAllowed (jsr250Enabled) is the JSR-250/Jakarta standard equivalent, also a plain role list, and its portability across frameworks is its main appeal. I default to @PreAuthorize for real-world rules, and reserve @Secured/@RolesAllowed for trivial role-only checks or when matching an existing convention/standard. You can enable several families at once since they attach to different methods.
code
java · 18 lines@Service
public class DocService {
@PreAuthorize("#doc.owner == authentication.name or hasRole('ADMIN')")
public void update(Document doc) { }
@PostAuthorize("returnObject.owner == authentication.name")
public Document load(String id) { return repo.find(id); }
@PostFilter("filterObject.owner == authentication.name")
public List<Document> listMine() { return repo.findAll(); }
@Secured("ROLE_ADMIN") // simple, Spring-specific
public void purge() { }
@RolesAllowed("ADMIN") // simple, JSR-250 standard
public void archive() { }
}go deeper
Know @PreAuthorize is the flexible SpEL one and the other two are simple role lists.
Articulate the SpEL capabilities (args, returnObject, filtering) and the role-prefix nuance, and pick the right annotation per scenario.
Recommend a project-wide convention and explain why unrecognized annotations silently do nothing.
Weigh portability (JSR-250) vs. expressiveness, and govern consistent role/authority prefix handling across services.
## The three annotation families `@EnableMethodSecurity` can turn on three independent styles; each is toggled by its own flag. ### 1. Pre/Post — `prePostEnabled` (default **true**) Enables four SpEL-based annotations: - **`@PreAuthorize("expr")`** — evaluated *before* invocation. Full SpEL: `hasRole('ADMIN')`, `hasAuthority('SCOPE_read')`, `#id == authentication.name`, `@myChecker.allows(#doc)`. - **`@PostAuthorize("expr")`** — evaluated *after*; can reference the return value via `returnObject`, e.g. `returnObject.owner == authentication.name`. - **`@PreFilter`** — mutates/filters a **collection or array argument**, keeping only elements matching the expression (`filterObject`). - **`@PostFilter`** — filters the **returned** collection element-by-element. This family is the most expressive and is the recommended default. ### 2. `@Secured` — `securedEnabled` (default false) Legacy Spring Security annotation taking a **list of authority strings**: `@Secured({"ROLE_ADMIN", "ROLE_OPS"})`. Semantics: caller needs **any one** of the listed authorities. **No SpEL**, no argument access, and by convention values are the raw authority names (often `ROLE_`-prefixed). ### 3. `@RolesAllowed` — `jsr250Enabled` (default false) The **JSR-250 / Jakarta** standard (`jakarta.annotation.security`): `@RolesAllowed("ADMIN")`, plus `@PermitAll` (always allow) and `@DenyAll` (always deny). Like `@Secured` it is a plain role list, but it is a **standard** annotation, so it is portable to Jakarta EE / other frameworks. Spring maps the role to the `ROLE_` authority via the configured role prefix. ## Choosing between them | Need | Use | |------|-----| | Argument/return/principal-aware or custom logic | `@PreAuthorize` / `@PostAuthorize` | | Filter a collection in/out | `@PreFilter` / `@PostFilter` | | Trivial single/multi-role gate, Spring-specific | `@Secured` | | Trivial role gate, framework-portable standard | `@RolesAllowed` | ## Gotchas - **Mixing on one method** is discouraged — order/interaction gets confusing. Standardize per project. - `@Secured`/`@RolesAllowed` compare against the user's **authorities**; a role `ADMIN` becomes authority `ROLE_ADMIN` through the role prefix. Get the prefix wrong and checks silently deny. - `hasRole('ADMIN')` in SpEL auto-adds the `ROLE_` prefix; `hasAuthority('ROLE_ADMIN')` does not — a frequent source of confusion. - Enabling a flag only makes the annotation *recognized*; unrecognized annotations are simply ignored (no error), which can mask a forgotten flag.
- What is the difference between hasRole('ADMIN') and hasAuthority('ADMIN') inside @PreAuthorize?hasRole prepends the configured role prefix (default ROLE_), so it checks for authority ROLE_ADMIN. hasAuthority checks the exact string 'ADMIN' with no prefix. Mixing them up causes unexpected denials.
- Can @PostAuthorize see the value the method returned?Yes — it runs after the method and exposes the result via the SpEL variable returnObject, so you can authorize based on the loaded entity, e.g. returnObject.owner == authentication.name.
saying these in an interview costs you the question
- Claiming @Secured supports SpEL or method-argument references.
- Thinking @RolesAllowed is Spring-specific (it's JSR-250/Jakarta standard).
- Assuming hasRole and hasAuthority are interchangeable.
- Believing @PreFilter filters the return value (that's @PostFilter).