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.
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.
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
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')expectsROLE_ADMIN' - '
hasAuthority('ADMIN')expects exactlyADMIN'
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.
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:
- method security enabled
- controller managed by Spring
- expression matches actual authorities
- authenticated principal is what you think it is
- 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
@EnableMethodSecurityor the older pre-post configuration. - Using
hasRole('ADMIN')when the user only hasADMINas 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
- '
@PreAuthorizerequires 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
- Prevent inter-namespace communication in Kubernetes
- Prevent Kubernetes users from being able to create privileged containers
- Prevent screen capture in an iOS app
- Privileged containers and capabilities
- Preferred Java way to ping an HTTP URL for availability
- PreparedStatement IN clause alternatives?
- problem path for truststore inside docker with spring boot and kafka
- Problems using Maven and SSL behind proxy

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack 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.