RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Spring Boot questions
TrickySpring Boot#516 min readJul 11, 2026

Role-Based Authorization.

authorization
roles
spring-security
Practice modeTest yourself instead of reading straight through

Why interviewers love this: it quickly reveals whether you know the difference between who you are and what you may do.

Question: What is role-based authorization in Spring Boot?

Answer: Role-based authorization means a logged-in user can access certain endpoints or methods only if they have the right role, such as ADMIN or USER. In Spring Security, roles are usually represented as authorities with the default ROLE_ prefix, so hasRole('ADMIN') checks for ROLE_ADMIN. This lets you protect admin screens, support tools, and sensitive APIs in a simple, consistent way.

Interview-Ready Answer: In Spring Boot, I use role-based authorization to decide what an authenticated user is allowed to do. I usually configure it with Spring Security using hasRole('ADMIN') or @PreAuthorize, and I remember that Spring adds the ROLE_ prefix automatically, so hasRole('ADMIN') really checks for ROLE_ADMIN. In practice, I use roles for coarse-grained access like admin versus user, and I enforce them on the server so hiding buttons in the UI is never the only security control.

🧠 Memory Map
Memory map — visual summary of this topic

What it is

Authorization is the permission step. Authentication answers, “Who are you?”; authorization answers, “What can you do?” A role is a coarse label such as ADMIN, USER, or AUDITOR. It is best for broad access decisions, not tiny permissions on every field.

How it works under the hood

  1. A request arrives with credentials, usually Basic Auth, a session cookie, or a JWT token. Spring Security authenticates the user first and builds an Authentication object.
  2. That Authentication is stored in the SecurityContext, which is the per-request holder for security data.
  3. Spring then evaluates your rules from HttpSecurity or method annotations such as @PreAuthorize. These rules are the gates.
  4. hasRole('ADMIN') is a convenience check: Spring converts it to the authority ROLE_ADMIN using the default prefix.
  5. If the user has the needed role, the request continues. If not, Spring returns 403 Forbidden. If the user is not logged in at all, it is usually 401 Unauthorized.
  6. For method security, Spring intercepts the method call before your code runs, so the business method never executes for a denied user.

Roles vs. authorities

ConceptMeaningExample
RoleCoarse labelADMIN
AuthorityExact permissionROLE_ADMIN
hasRoleAdds prefixADMIN -> ROLE_ADMIN
hasAuthorityExact matchROLE_ADMIN

That table is the main gotcha: if you store roles with roles('ADMIN'), Spring stores the authority as ROLE_ADMIN. If you instead use authorities('ADMIN'), no prefix is added, so your checks must match exactly.

When and why to use it

Use role-based authorization when the business question is simple: “Can only admins access this page?” or “Can support agents see this endpoint?” It is a clean fit for dashboards, back-office apps, internal tools, and APIs where access falls into a few clear buckets. If you need highly specific checks like “can refund order but not edit address,” you may outgrow roles and move toward permissions or policy-based checks.

Performance and version notes

Authorization checks are usually very fast. Rule matching is roughly linear in the number of configured matchers, but in real apps that is usually a handful to a few dozen rules, so the cost is tiny compared with database lookups or JWT signature verification. In Spring Boot 3 / Spring Security 6, the modern APIs are requestMatchers and @EnableMethodSecurity; older examples on the internet may still show antMatchers or @EnableGlobalMethodSecurity.

Important edge cases

  • Do not write hasRole('ROLE_ADMIN'); Spring adds the prefix for you.
  • Do not rely on the front end to enforce roles; the server must check them every time.
  • Be clear about 401 vs 403: no login means 401, wrong role means 403.
  • If you want ADMIN to automatically include USER access, use a role hierarchy instead of duplicating checks everywhere.

Real-world story

Imagine an e-commerce checkout service with an internal admin dashboard. Customer support needs to view orders, finance needs to issue refunds, and operations needs to see audit reports. The team uses roles like USER, SUPPORT, and ADMIN so each group gets only the screens and APIs they need. That keeps the code readable and makes audits easier because access is grouped by job function, not by dozens of tiny one-off rules.

Here is a common production incident: a developer stores users with roles('ADMIN'), which creates ROLE_ADMIN, but later writes a guard with hasAuthority('ADMIN') instead of hasRole('ADMIN'). Suddenly every admin gets 403 Forbidden on the admin report page, the button spins forever in the UI, and logs fill with AccessDeniedException. From the outside it looks like an outage, but the root cause is just a prefix mismatch. The fix is small; the business impact is not, because support cannot act and tickets pile up fast.

Spring Boot
package com.example.rolesauth;

import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class RoleBasedAuthorizationApplication {

    public static void main(String[] args) {
        SpringApplication.run(RoleBasedAuthorizationApplication.class, args);
    }
}

@Configuration
@EnableMethodSecurity
class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable()) // Keep the demo simple; browser-based POST/PUT/DELETE apps usually keep CSRF enabled.
            .httpBasic(Customizer.withDefaults())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated());
        return http.build();
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    UserDetailsService userDetailsService(PasswordEncoder encoder) {
        // roles("ADMIN") stores the authority as ROLE_ADMIN under the hood.
        UserDetails admin = User.withUsername("admin")
            .password(encoder.encode("admin123"))
            .roles("ADMIN")
            .build();

        UserDetails alice = User.withUsername("alice")
            .password(encoder.encode("alice123"))
            .roles("USER")
            .build();

        UserDetails auditor = User.withUsername("auditor")
            .password(encoder.encode("audit123"))
            .roles("AUDITOR", "USER")
            .build();

        return new InMemoryUserDetailsManager(admin, alice, auditor);
    }
}

@RestController
class DemoController {

    @GetMapping("/public/ping")
    public Map<String, String> ping() {
        return Map.of("status", "ok");
    }

    @GetMapping("/user/profile")
    @PreAuthorize("hasAnyRole('USER', 'ADMIN')")
    public Map<String, String> profile(Authentication authentication) {
        return Map.of(
            "message", "User area",
            "user", authentication.getName()
        );
    }

    @GetMapping("/admin/report")
    @PreAuthorize("hasRole('ADMIN')")
    public Map<String, String> adminReport(Authentication authentication) {
        // If alice (ROLE_USER) calls this, Spring Security stops the request with 403 Forbidden.
        // That is authorization: the user is authenticated, but not allowed for this role.
        return Map.of(
            "message", "Admin-only report",
            "user", authentication.getName()
        );
    }

    @GetMapping("/whoami")
    public Map<String, Object> whoAmI(Authentication authentication) {
        return Map.of(
            "user", authentication.getName(),
            "authorities", authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList())
        );
    }
}

Follow-up & Tricky Questions:

  • What is the difference between hasRole and hasAuthority?
    hasRole('ADMIN') adds the default ROLE_ prefix and checks ROLE_ADMIN. hasAuthority does an exact string match, so it is what you use when you already store the full authority name yourself.
  • Where do roles come from in a real app?
    They may come from the database, an LDAP directory, a JWT claim, or an identity provider. Spring Security turns them into GrantedAuthority objects on the Authentication.
  • Why do I get 401 sometimes and 403 other times?
    401 means the user is not authenticated yet. 403 means the user is authenticated but lacks the required role.
  • How do I secure service-layer methods, not just URLs?
    Use @PreAuthorize on service or controller methods with @EnableMethodSecurity. That gives you defense in depth, so the rule is enforced even if the endpoint is called from a different path.
  • Can one user have multiple roles?
    Yes. Spring Security stores authorities as a collection, so one user can have USER, SUPPORT, and AUDITOR together.
  • Trick: If I use roles('ADMIN'), what is actually stored?
    Spring stores ROLE_ADMIN, not plain ADMIN. That is why hasRole('ADMIN') works and hasAuthority('ADMIN') does not.
  • Trick: Should I write hasRole('ROLE_ADMIN')?
    No. That doubles the prefix logic and usually fails. The correct call is hasRole('ADMIN').
  • Trick: Does permitAll disable method security?
    No. It only opens the web path at the filter-chain level. If the controller or service method has @PreAuthorize, that method can still deny access later.

Common Mistakes:

  • Confusing authentication with authorization. Correction: authentication proves identity; authorization decides access.
  • Using the wrong prefix. Correction: with hasRole('ADMIN'), pass ADMIN, not ROLE_ADMIN.
  • Relying on the UI only. Correction: hide buttons for UX, but always enforce roles on the server.
  • Making roles too fine-grained. Correction: keep roles coarse; use authorities or policies for very specific actions.

Memory Hook: Think of a role as a locker label and an authority as the actual cut key. The label tells you which group you belong to; the key is what really opens the door.

Cheat Sheet:

  • roles('ADMIN') becomes ROLE_ADMIN.
  • hasRole('ADMIN') checks for ROLE_ADMIN.
  • hasAuthority('ROLE_ADMIN') is an exact match.
  • @EnableMethodSecurity is the modern Spring Security 6 option.
  • requestMatchers is the modern URL rule API in Boot 3.
  • Use roles for broad access; use permissions/authorities for fine-grained control.

Practice Tasks:

  • Add a /manager/** route and allow both ADMIN and MANAGER.
  • Change one endpoint from hasRole to hasAuthority and observe which user starts failing.
  • Create a JWT or database-backed user store and inspect the exact authorities returned by /whoami.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

package com.example.rolesauth; import java.util.Map; import java.util.stream.Collectors; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class RoleBasedAuthorizationApplication { public static void main(String[] args) { SpringApplication.run(RoleBasedAuthorizationApplication.class, args); } } @Configuration @EnableMethodSecurity class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) // Keep the demo simple; browser-based POST/PUT/DELETE apps usually keep CSRF enabled. .httpBasic(Customizer.withDefaults()) .authorizeHttpRequests(auth -> auth .requestMatchers("/public/**").permitAll() .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated()); return http.build(); } @Bean PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } @Bean UserDetailsService userDetailsService(PasswordEncoder encoder) { // roles("ADMIN") stores the authority as ROLE_ADMIN under the hood. UserDetails admin = User.withUsername("admin") .password(encoder.encode("admin123")) .roles("ADMIN") .build(); UserDetails alice = User.withUsername("alice") .password(encoder.encode("alice123")) .roles("USER") .build(); UserDetails auditor = User.withUsername("auditor") .password(encoder.encode("audit123")) .roles("AUDITOR", "USER") .build(); return new InMemoryUserDetailsManager(admin, alice, auditor); } } @RestController class DemoController { @GetMapping("/public/ping") public Map<String, String> ping() { return Map.of("status", "ok"); } @GetMapping("/user/profile") @PreAuthorize("hasAnyRole('USER', 'ADMIN')") public Map<String, String> profile(Authentication authentication) { return Map.of( "message", "User area", "user", authentication.getName() ); } @GetMapping("/admin/report") @PreAuthorize("hasRole('ADMIN')") public Map<String, String> adminReport(Authentication authentication) { // If alice (ROLE_USER) calls this, Spring Security stops the request with 403 Forbidden. // That is authorization: the user is authenticated, but not allowed for this role. return Map.of( "message", "Admin-only report", "user", authentication.getName() ); } @GetMapping("/whoami") public Map<String, Object> whoAmI(Authentication authentication) { return Map.of( "user", authentication.getName(), "authorities", authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.toList()) ); } }