skip to content

What is the difference between @Secured and @RolesAllowed regarding the ROLE_ prefix and role naming?

level: middleimportance: must knowfreq 50%

answer

  1. @RolesAllowed adds ROLE_
  2. @Secured literal, no prefix
  3. @Secured("ADMIN") = silent 403
  4. @Secured can match SCOPE_ authorities
  5. prefix set by GrantedAuthorityDefaults

basics

~10 s

@RolesAllowed takes the bare role name ("ADMIN") and Spring adds the ROLE_ prefix. @Secured takes the full authority string, so you must write "ROLE_ADMIN" yourself. Forgetting the prefix on @Secured silently denies access.

solid answer

~40 s

@RolesAllowed("ADMIN") is treated as a role: Spring's Jsr250AuthorizationManager applies the configured role prefix (default ROLE_), so it matches the authority ROLE_ADMIN — same convention as hasRole('ADMIN') in SpEL. @Secured is lower-level authority matching with no prefix logic at all: @Secured("ROLE_ADMIN") matches authority ROLE_ADMIN, and @Secured("ADMIN") matches a literal authority named ADMIN (which usually doesn't exist, so the user is denied). This means @Secured can also gate on non-role authorities like @Secured("SCOPE_read"), whereas @RolesAllowed is conceptually role-only. The practical gotcha: mixing the two in one codebase invites prefix mistakes, so teams usually standardize on one. The default ROLE_ prefix is configurable via a GrantedAuthorityDefaults bean.

code

java · 9 lines
java
// User has authority: ROLE_ADMIN

@RolesAllowed("ADMIN")     // -> checks ROLE_ADMIN  -> GRANTED
@Secured("ROLE_ADMIN")     // -> checks ROLE_ADMIN  -> GRANTED
@Secured("ADMIN")          // -> checks ADMIN       -> DENIED (403)

// Non-role authority use only @Secured can express:
@Secured("SCOPE_reports:read")
public Report load() { ... }

go deeper

for a junior

Just remember: @RolesAllowed no prefix, @Secured full prefix.

for a middle

Explain the silent-403 bug and the hasRole vs hasAuthority analogy.

for a senior

Discuss configurable prefix via GrantedAuthorityDefaults and using @Secured for scope authorities.

for a principal

Argue for standardizing one style codebase-wide to prevent prefix drift bugs.

## The core distinction The two annotations sit at different conceptual levels: - **@RolesAllowed** is *role-oriented*. You give a role **name**, and Spring's `Jsr250AuthorizationManager` prepends the configured **role prefix** (default `ROLE_`) before matching. So `@RolesAllowed("ADMIN")` checks for authority `ROLE_ADMIN`. This mirrors the SpEL `hasRole('ADMIN')` convention. - **@Secured** is *authority-oriented*. It performs a raw match against the user's `GrantedAuthority` strings with **no prefix processing**. `@Secured("ROLE_ADMIN")` matches `ROLE_ADMIN`; `@Secured("ADMIN")` matches a literal `ADMIN` authority. ## Why this matters Because @Secured does no prefixing, the single most common bug is: ```java @Secured("ADMIN") // BUG: looks for authority 'ADMIN', user has 'ROLE_ADMIN' -> 403 ``` This fails silently at runtime (a 403), not at compile or startup time, so it's easy to ship. Conversely, because @Secured matches *any* authority string, it can gate on non-role authorities: ```java @Secured("SCOPE_reports:read") // OAuth2 scope authority @Secured("ROLE_ADMIN") ``` @RolesAllowed can't cleanly do this — it always applies the role prefix, so it's designed for roles only. ## The role prefix is configurable The `ROLE_` default comes from `GrantedAuthorityDefaults`. You can override it: ```java @Bean static GrantedAuthorityDefaults grantedAuthorityDefaults() { return new GrantedAuthorityDefaults("MYPREFIX_"); } ``` After this, `@RolesAllowed("ADMIN")` and `hasRole('ADMIN')` look for `MYPREFIX_ADMIN`, while `@Secured("ROLE_ADMIN")` still matches the literal `ROLE_ADMIN` — a subtle inconsistency that's another reason not to mix styles. ## Consistency with hasRole vs hasAuthority A useful mental map: - `@RolesAllowed("X")` ≈ `@PreAuthorize("hasRole('X')")` — prefix added. - `@Secured("ROLE_X")` ≈ `@PreAuthorize("hasAuthority('ROLE_X')")` — literal match. ## Recommendation Standardize on one style per codebase. @RolesAllowed is often preferred because its no-prefix naming matches `hasRole`, it's a portable Java standard, and it avoids the drop-the-prefix bug. Use @Secured when you deliberately want to match arbitrary authority strings (including scopes) without role semantics.

  • How would you change the default ROLE_ prefix, and what does it affect?
    Define a static GrantedAuthorityDefaults bean with the new prefix. It affects hasRole and @RolesAllowed matching, but not @Secured literal strings, which is why mixing them after a prefix change is dangerous.
  • Which annotation would you use to authorize on an OAuth2 scope authority like SCOPE_read?
    @Secured("SCOPE_read") or @PreAuthorize("hasAuthority('SCOPE_read')") — @RolesAllowed is role-only and would incorrectly prefix it as ROLE_SCOPE_read.

saying these in an interview costs you the question

  • Claiming @Secured and @RolesAllowed behave identically with role names
  • Saying @Secured("ADMIN") works because Spring adds ROLE_
  • Not realizing the ROLE_ prefix is configurable

context