skip to content

What are the semantics when @Secured or @RolesAllowed list multiple roles, and what can't they express?

level: middleimportance: should knowfreq 38%

answer

  1. multiple values = OR / any-of
  2. no AND, no NOT, no args
  3. adding entries only widens access
  4. AND -> @PreAuthorize hasRole and hasRole
  5. set-intersection test

basics

~20 s

Multiple listed roles mean OR — having any one of them grants access. Neither annotation can express AND (require several roles at once), negation, or any condition based on method arguments. For those you need @PreAuthorize with SpEL.

solid answer

~40 s

Both @Secured({"ROLE_A","ROLE_B"}) and @RolesAllowed({"A","B"}) use OR / any-of semantics: the caller passes if they hold at least one of the listed authorities. There is no way to require multiple roles simultaneously (AND), to negate a role (NOT), or to reference method parameters — the check is a pure set-membership test against the user's GrantedAuthority collection. If you need 'ADMIN and MFA_VERIFIED', ownership checks, or scope+role combinations, you must switch to @PreAuthorize("hasRole('ADMIN') and hasRole('MFA')") or a custom expression. A subtle point: since they're OR-only, a longer list only ever widens access — it can never narrow it. This makes reviewing them easy but also means you can't tighten a rule by adding entries.

code

java · 7 lines
java
// any-of: admin OR support may call
@Secured({"ROLE_ADMIN", "ROLE_SUPPORT"})
public void handleTicket(Long id) { ... }

// need BOTH roles? annotations can't -> use SpEL
@PreAuthorize("hasRole('ADMIN') and hasRole('AUDITOR')")
public void closeQuarter() { ... }

go deeper

for a junior

Remember multiple roles = any one is enough (OR).

for a middle

State clearly it's OR-only and name AND/NOT/args as the gaps needing @PreAuthorize.

for a senior

Note the 'adding entries only widens access' review property.

for a principal

Use the OR-only limitation to decide where a codebase must standardize on @PreAuthorize.

## OR semantics When you supply multiple values, both annotations grant access if the user has **any** of them: ```java @Secured({"ROLE_ADMIN", "ROLE_SUPPORT"}) // admin OR support @RolesAllowed({"ADMIN", "SUPPORT"}) // admin OR support ``` Internally the check is: *does the user's authority set intersect the annotation's set?* One match is enough. ## What they cannot express 1. **AND (conjunction).** You cannot say 'must be ADMIN *and* AUDITOR'. Listing both only OR's them. Use `@PreAuthorize("hasRole('ADMIN') and hasRole('AUDITOR')")`. 2. **NOT (negation).** No 'must not be GUEST'. Use SpEL `!hasRole('GUEST')`. 3. **Argument-based rules.** No access to `#id`, the principal's fields, or method parameters. SpEL only. 4. **Custom permissions / bean calls.** No `hasPermission(...)` or `@service.check(#x)`. 5. **Post-invocation / filtering.** No @PostAuthorize/@PreFilter/@PostFilter analog. ## Consequence for review and evolution Because the list is OR-only, **adding an entry can only broaden access, never restrict it**. That's a nice property for reasoning about who's allowed, but it also means you can't 'tighten' an @Secured rule by editing its list — tightening to an AND requires moving to @PreAuthorize. Reviewers should treat every added role as a new door opened. ## Empty / misused values - An empty `@RolesAllowed({})` effectively allows no one (no authority can match), which is rarely intended. - Duplicated or misspelled entries don't error; they just never match, so a typo silently reduces who's allowed to the remaining valid entries. ## Practical example of the boundary ```java // OR — fine with @RolesAllowed @RolesAllowed({"ADMIN", "BILLING"}) public Invoice view(Long id) { ... } // AND + argument — must be @PreAuthorize @PreAuthorize("hasRole('ADMIN') and #invoice.orgId == authentication.principal.orgId") public void approve(Invoice invoice) { ... } ```

  • How do you require a user to hold two roles simultaneously?
    You can't with @Secured/@RolesAllowed — they're OR-only. Use @PreAuthorize("hasRole('A') and hasRole('B')").
  • If you add a role to an existing @Secured list, can that ever restrict access?
    No. OR semantics mean adding an entry can only widen who's allowed, never narrow it.

saying these in an interview costs you the question

  • Believing multiple listed roles are AND-ed (all required)
  • Thinking you can negate a role inside @Secured/@RolesAllowed
  • Expecting to reference method arguments in these annotations

context