Chapter 7.4☕ 14 min read

Role Based Access: @PreAuthorize

URL rules secure the gates. @PreAuthorize secures the individual rooms.

01The Concept: Method-Level Authorization

The Hospital Operation Theater Analogy:

In a hospital, the main gate security checks if you are a valid doctor or patient (URL Security). But when you try to perform a surgery in the Operation Theater, a second check happens: Are you a Surgeon? A nurse is allowed in the hospital, but not allowed to perform surgery.

In Spring Boot, @PreAuthorize is that second check. It happens right before the Java method executes. It checks the user’s role before allowing the code to run.

02Technical Explanation
  1. @EnableMethodSecurity: In Spring Security 6, this replaces @EnableGlobalMethodSecurity. It turns on the engine that reads @PreAuthorize annotations.
  2. @PreAuthorize("hasRole('ADMIN')"): Places a lock on the method. Only users whose JWT contains the ADMIN role can enter.
  3. Roles vs Authorities: In Spring Security, a Role is prefixed with ROLE_ (e.g., ROLE_ADMIN). When using hasRole('ADMIN'), Spring automatically adds the ROLE_ prefix.
03Full Working Code: Securing Methods

First, enable method security on your main class.

package com.devinhyderabad;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@SpringBootApplication
@EnableMethodSecurity // Replaces @EnableGlobalMethodSecurity in SB3
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Now, apply it to your controller methods.

package com.devinhyderabad;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/orders")
public class OrderController {

// 1. Anyone with a valid JWT (USER or ADMIN) can access this
@GetMapping("/{id}")
public String getOrder(@PathVariable Long id) {
return "Order details for " + id;
}

// 2. ONLY users with the ADMIN role can delete
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public String deleteOrder(@PathVariable Long id) {
return "Order " + id + " deleted by Admin!";
}

// 3. Multiple roles allowed
@PutMapping("/{id}")
@PreAuthorize("hasAnyRole('ADMIN', 'MANAGER')")
public String updateOrder(@PathVariable Long id) {
return "Order updated!";
}
}

If a regular USER tries to call DELETE /api/orders/1, Spring Security throws an AccessDeniedException (403 Forbidden) before the deleteOrder method even executes.

04Code Walkthrough

The setup is simple:

  • @EnableMethodSecurity: Enables Spring Security’s annotation-based security. This is a one-time setup on your @SpringBootApplication class.
  • @PreAuthorize with SpEL: The annotation accepts Spring Expression Language (SpEL). hasRole('ADMIN') checks if the authenticated user has the ROLE_ADMIN authority.
  • hasAnyRole: For scenarios where multiple roles should have access (e.g., both ADMIN and MANAGER can update orders).

Note: For @PreAuthorize to work with roles, your JwtAuthenticationFilter must extract roles from the JWT and pass them as GrantedAuthority objects into the UsernamePasswordAuthenticationToken.

05Why It Matters / Interview Note

Interview Question: “What is the difference between @Secured and @PreAuthorize?”

Answer: @Secured("ROLE_ADMIN") is older and only supports checking simple roles. @PreAuthorize("hasRole('ADMIN') and #order.owner == authentication.name") uses Spring Expression Language (SpEL). It is much more powerful because you can write complex logic, like “Allow access only if the user is the owner of this order”.

Enterprise Note: To make @PreAuthorize work with roles, your JwtAuthenticationFilter must extract the roles from the JWT and pass them as GrantedAuthority objects into the UsernamePasswordAuthenticationToken. If your filter only sets the username and no authorities, hasRole('ADMIN') will always fail.

Key Takeaways

  • ✅ @PreAuthorize provides method-level security beyond basic URL rules
  • ✅ @EnableMethodSecurity replaces @EnableGlobalMethodSecurity in Spring Security 6
  • ✅ Roles get auto-prefixed with ROLE_ when using hasRole()
  • ✅ SpEL in @PreAuthorize supports complex conditions (owner checks, etc.)
  • ✅ JwtAuthenticationFilter must pass GrantedAuthority objects for role checks