skip to content

Compare @PreAuthorize (prePostEnabled) with @Secured (securedEnabled) and @RolesAllowed (jsr250Enabled): when would you choose each?

level: middleimportance: should knowfreq 55%

answer

  1. PreAuthorize = SpEL (args, principal, custom beans)
  2. PostAuthorize = returnObject; Pre/PostFilter = collections
  3. @Secured / @RolesAllowed = plain role list, no SpEL
  4. JSR-250 = portable standard (@PermitAll/@DenyAll)
  5. 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 s

All 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
java
@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

for a junior

Know @PreAuthorize is the flexible SpEL one and the other two are simple role lists.

for a middle

Articulate the SpEL capabilities (args, returnObject, filtering) and the role-prefix nuance, and pick the right annotation per scenario.

for a senior

Recommend a project-wide convention and explain why unrecognized annotations silently do nothing.

for a principal

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).

context