Authorization

Implement role-based and permission-based authorization in Spring Boot using @PreAuthorize, @PostAuthorize, URL-based security, and custom authorization logic.

Last reviewed: January 2025

·Spring Boot 2.7Spring Boot 3.xSpring Security 6
✅
Quick Understanding: Authorization determines what an authenticated user can do. Spring Security supports two levels: URL-based (in SecurityFilterChain: .requestMatchers("/admin/**").hasRole("ADMIN")) and method-based (@PreAuthorize("hasRole('ADMIN')") on service or controller methods). Method-based is more flexible and is preferred for complex rules.

🔍 What Is It?

Authorization in Spring Security operates at two levels:

URL-based authorization (SecurityFilterChain)

  • →Applied before the request reaches the controller
  • →Good for coarse-grained rules (admin paths, public paths)
  • →Pattern matching on URL and HTTP method

Method-based authorization (@PreAuthorize, @PostAuthorize)

  • →Applied at the method level via AOP proxy
  • →Uses Spring Expression Language (SpEL)
  • →Can access method parameters and return values
  • →More granular than URL-based

Spring Security expressions:

  • →hasRole('ADMIN') — has ROLE_ADMIN authority
  • →hasAnyRole('USER', 'ADMIN') — has any of these roles
  • →hasAuthority('READ_ORDERS') — has specific permission
  • →isAuthenticated() — any authenticated user
  • →isAnonymous() — unauthenticated user
  • →#id == authentication.principal.id — parameter check

🎯 Why Do We Need It?

Without authorization, any authenticated user can access any endpoint. Authorization enforces the principle of least privilege — users only access what they're permitted to. In enterprise apps, this means admins can manage users, regular users can only see their own data, and service accounts have limited scoped access.

🏗️ What Happens in Real Projects?

Enterprise authorization patterns:

  • →Role-based: ADMIN, USER, MODERATOR, SUPPORT roles
  • →Permission-based: READ_ORDERS, WRITE_ORDERS, DELETE_ORDERS
  • →Resource-based: users can only access their own resources
  • →Tenant-based: in multi-tenant apps, users only access their tenant's data

⚙️ How It Works

@PreAuthorize is processed by AOP (MethodSecurityInterceptor). Before the method runs, Spring evaluates the SpEL expression with the current Authentication. If it returns false, AccessDeniedException is thrown (403). @PostAuthorize runs after the method and can check the return value.

💻 Example

Authorization Patterns — URL security, method security, and custom authorization.

java
// URL-based in SecurityConfig
.authorizeHttpRequests(auth -> auth
    .requestMatchers(HttpMethod.GET, "/api/v1/products/**").permitAll()
    .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
    .requestMatchers("/api/v1/reports/**").hasAnyRole("ADMIN", "ANALYST")
    .requestMatchers(HttpMethod.DELETE).hasRole("ADMIN")  // all DELETE requires ADMIN
    .anyRequest().authenticated()
)

// Method-based in controller/service
@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    @GetMapping
    @PreAuthorize("hasRole('ADMIN')")
    public Page<UserDto> getAllUsers(Pageable pageable) { ... }

    // User can only get their own profile (or admin can get any)
    @GetMapping("/{id}")
    @PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')")
    public UserDto getUser(@PathVariable Long id) { ... }

    @DeleteMapping("/{id}")
    @PreAuthorize("hasRole('ADMIN')")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable Long id) { ... }

    // Check permission on return value
    @GetMapping("/{id}/sensitive")
    @PostAuthorize("returnObject.id == authentication.principal.id or hasRole('ADMIN')")
    public UserDetailDto getSensitiveData(@PathVariable Long id) { ... }
}

🤔 Common Confusions

❓ hasRole vs hasAuthority — what is the difference?

hasRole("ADMIN") checks for authority "ROLE_ADMIN" (Spring automatically prepends "ROLE_"). hasAuthority("ADMIN") checks for authority "ADMIN" exactly. Use hasRole for roles (ADMIN, USER), hasAuthority for fine-grained permissions (READ_ORDERS, WRITE_PRODUCTS). Be consistent in your codebase.

❓ Can I use @PreAuthorize on private methods?

No. Spring Security uses AOP proxies, which only intercept public methods. @PreAuthorize on private or package-private methods is silently ignored. If you need to secure private methods, redesign so the security check is on the public method that calls them.

🚨 Common Production Issues

@PreAuthorize on private methods being silently ignored

Method security not working because @EnableMethodSecurity is missing from configuration

Authorization bypassed via self-invocation (same as @Transactional — proxy bypassed)

Overly permissive rules: anyRequest().permitAll() left from development

✅ Best Practices

Enable @EnableMethodSecurity in your security configuration

Use @PreAuthorize at the service layer, not just the controller layer

Implement PermissionEvaluator for complex, resource-specific authorization

Test authorization with @WithMockUser(roles="ADMIN") and negative test cases

Fail securely: default to deny, explicitly permit what's allowed

🏛️ Architect's Note

Senior Architect's Perspective

In complex multi-tenant or multi-role systems, consider Attribute-Based Access Control (ABAC) over simple RBAC. ABAC makes decisions based on attributes of the user, resource, and environment. Spring Security's SpEL in @PreAuthorize can implement ABAC patterns. For very complex authorization, consider dedicated libraries like Spring Authorization Server or Casbin.

💬 Frequently Asked Questions

How do I implement row-level security (users only see their own data)?

Several approaches: (1) @PreAuthorize with parameter check: @PreAuthorize("#userId == authentication.principal.id"). (2) In the service, always filter by the current user: repository.findByIdAndUserId(id, currentUserId). (3) Use Spring Data JPA @Query with :#{principal.username} SpEL. Approach 2 is most reliable — it's enforced at the data layer.

🎯 Key Takeaways

1URL-based security in SecurityFilterChain for coarse-grained rules
2@PreAuthorize for fine-grained method-level authorization
3hasRole("ADMIN") checks for ROLE_ADMIN; hasAuthority("ADMIN") checks for ADMIN exactly
4@PreAuthorize only works on public methods (AOP proxy limitation)
5Implement PermissionEvaluator for complex resource-specific authorization
6Default to deny — explicitly permit what's allowed

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Spring Boot references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Spring Boot learning path

Browse the full Spring Boot topic collection and learning order on SplashRide.

Spring Boot Interview Prep

Practice Spring Boot interview questions and scenario-based answers.

Backend Engineer Career Path

A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Spring Security

Master Spring Security — the comprehensive security framework for authentication, authorization, and protection against common web vulnerabilities in Spring Boot applications.

Authentication

Implement authentication in Spring Boot — UserDetailsService, AuthenticationManager, login endpoints, and the complete authentication flow for enterprise REST APIs.