๐ด The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
org.springframework.security.access.prepost.PreInvocationExceptionAdvice
...
Caused by: java.lang.IllegalArgumentException: Failed to evaluate expression 'hasRole('ADMIN')'
at org.springframework.security.access.expression.method.MethodSecurityExpressionRoot.hasRole(MethodSecurityExpressionRoot.java:245)
Caused by: org.springframework.expression.spel.SpelEvaluationException
...โก Quick Fix Works 80% of the time
Add @EnableMethodSecurity to your SecurityConfig class.
@Configuration
@EnableMethodSecurity
public class SecurityConfig { ... }๐ง Why this Happens
Tap to expand the deep technical explanation
You used `@PreAuthorize` on a controller method, but Spring's method security engine is not turned on. In Spring Security 6, method security is disabled by default. Without the `@EnableMethodSecurity` annotation, Spring ignores `@PreAuthorize` or fails to evaluate the SpEL expression because the security root isn't initialized.
The HITEC City Parking Spot Analogy:
Imagine installing a fancy smart lock (@PreAuthorize) on your office door, but you forgot to connect it to the building's electricity (@EnableMethodSecurity). When someone tries to scan their badge, the lock throws an error because it has no power to evaluate the scan.
๐ How to Reproduce Confirm this is your error
Create a SecurityConfig class. Do NOT add `@EnableMethodSecurity`. Add `@PreAuthorize("hasRole('ADMIN')")` to a controller method. Call the endpoint.
๐ ๏ธ Solutions (5 Ways to Fix)
Add @EnableMethodSecurity annotation
๐ Use this as the primary fix when using @PreAuthorize or @PostAuthorize.
This annotation activates the AOP proxy that intercepts method calls and evaluates the security expressions.
@Configuration
@EnableMethodSecurity // This replaces @EnableGlobalMethodSecurity from SB2
public class SecurityConfig { ... }Fix SpEL Syntax Errors
๐ Use this if @EnableMethodSecurity is present but the error persists.
Ensure the expression string has correct quotes and parentheses. SpEL is very strict.
// BAD: missing quotes around ADMIN
// @PreAuthorize("hasRole(ADMIN)")
// GOOD
@PreAuthorize("hasRole('ADMIN')")Handle Nulls in SpEL
๐ Use this if your expression accesses method parameters that might be null.
If you use `#order.owner`, and `#order` is null, SpEL throws an error. Use the safe navigation operator `?.`.
// BAD: throws error if #order is null
// @PreAuthorize("#order.owner == authentication.name")
// GOOD: safe navigation
@PreAuthorize("#order?.owner == authentication.name")Ensure the method is public
๐ Use this if your annotation is on a private or protected method.
Spring AOP proxies only intercept public methods. If you put @PreAuthorize on a private method, it won't be evaluated and might throw weird context errors.
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) { ... } // MUST be publicUse @PreAuthorize on interface or class level correctly
๐ Use this if you want to secure an entire class.
Ensure you aren't overriding a secured method with an unsecured one in a subclass, which confuses the proxy.
@RestController
@PreAuthorize("hasRole('ADMIN')") // Secures all methods in class
public class AdminController { ... }๐ Version Notes
Uses `@EnableGlobalMethodSecurity(prePostEnabled = true)`.
Uses `@EnableMethodSecurity` (prePostEnabled is true by default).
๐ก๏ธ How to Prevent This Next Time
Always add `@EnableMethodSecurity` to your SecurityConfig the moment you add the Spring Security dependency, so it's ready when you need it.