PreAuthorize
Controller Issue
Authorization Error
Spring Security
Access Control

PreAuthorize not working on Controller

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

When @PreAuthorize appears to do nothing on a Spring controller, the problem is usually configuration rather than the annotation itself. Method security must be enabled, the controller must be a Spring-managed bean, and the security expression has to match the actual authorities in the authenticated user.

First Check: Is Method Security Enabled?

In modern Spring Security, @PreAuthorize only works if method security is turned on.

java
1import org.springframework.context.annotation.Configuration;
2import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
3
4@Configuration
5@EnableMethodSecurity
6public class SecurityConfig {
7}

In older Spring Security versions, the equivalent was @EnableGlobalMethodSecurity(prePostEnabled = true). If this configuration is missing, Spring will happily start and simply never enforce your @PreAuthorize annotations.

Typical Controller Example

java
1import org.springframework.security.access.prepost.PreAuthorize;
2import org.springframework.web.bind.annotation.GetMapping;
3import org.springframework.web.bind.annotation.RequestMapping;
4import org.springframework.web.bind.annotation.RestController;
5
6@RestController
7@RequestMapping("/admin")
8public class AdminController {
9
10    @PreAuthorize("hasRole('ADMIN')")
11    @GetMapping("/reports")
12    public String reports() {
13        return "secret data";
14    }
15}

If the current user lacks the required role, access should be denied before the method body runs.

The Role Prefix Problem

One of the most common mistakes is using hasRole('ADMIN') while the authenticated user actually has the authority ADMIN instead of ROLE_ADMIN.

By default:

  • 'hasRole('ADMIN') expects ROLE_ADMIN'
  • 'hasAuthority('ADMIN') expects exactly ADMIN'

So if your tokens or user details contain plain authorities without the ROLE_ prefix, this annotation will fail logically even though method security is active.

java
@PreAuthorize("hasAuthority('ADMIN')")

Use the expression that matches how your application stores authorities.

Make Sure Spring Is Calling the Bean

@PreAuthorize is applied through Spring's method-security infrastructure. If you instantiate the controller yourself with new, the annotation will not run because Spring is bypassed.

This also matters in tests. If you construct a controller directly and call the method like a plain Java object, no security interception happens. To verify @PreAuthorize, test through Spring MVC or a Spring-managed bean context.

Request Rules and Method Rules Work Together

Another point of confusion is mixing request authorization rules with method authorization rules. For example, you might allow a URL pattern through the HTTP layer and still expect @PreAuthorize to block the controller method. That is valid. The request matcher and the method annotation are different layers.

If method security is enabled, permitAll() at the request layer does not automatically disable @PreAuthorize.

Debugging the Real Cause

When the annotation seems ineffective, check these in order:

  1. method security enabled
  2. controller managed by Spring
  3. expression matches actual authorities
  4. authenticated principal is what you think it is
  5. tests are going through the Spring security layer

Enabling Spring Security debug logging can also help you see which authorities are actually present at runtime.

Prefer Service-Level Security for Business Rules

You can place @PreAuthorize on controllers, and it works, but many teams prefer to enforce important business rules at the service layer as well. That prevents accidental bypass through non-HTTP entry points such as schedulers, messaging consumers, or internal method calls from other parts of the application.

Controller-level security is useful, but service-level security is often the stronger boundary.

Common Pitfalls

  • Forgetting @EnableMethodSecurity or the older pre-post configuration.
  • Using hasRole('ADMIN') when the user only has ADMIN as an authority.
  • Testing a controller by instantiating it directly instead of letting Spring manage it.
  • Assuming URL security rules are the same thing as method security rules.
  • Putting security annotations in place without checking what authorities are actually loaded into the authentication object.

Summary

  • '@PreAuthorize requires method security to be enabled explicitly.'
  • The controller must be invoked as a Spring-managed bean for the annotation to run.
  • Role-prefix mismatches are a very common cause of incorrect behavior.
  • Method security and request security are separate layers.
  • If the rule is important to the business domain, consider enforcing it in services as well as controllers.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.