Why did @EnableMethodSecurity replace @EnableGlobalMethodSecurity, and what changed?
answer
- Old = @EnableGlobalMethodSecurity (deprecated)
- New = @EnableMethodSecurity (5.6+)
- AuthorizationManager replaces AccessDecisionManager/voters
- prePostEnabled default flipped to true
- customize via AuthorizationManager beans, not GlobalMethodSecurityConfiguration subclass
basics
~20 s@EnableMethodSecurity (Spring Security 5.6+) replaces the deprecated @EnableGlobalMethodSecurity. It's built on the newer, simpler AuthorizationManager API instead of the old AccessDecisionManager/voter machinery, defaults prePostEnabled to true, and supports meta-annotations and custom SpEL beans more cleanly.
solid answer
~40 s@EnableGlobalMethodSecurity is the old (now deprecated) way to enable method security; @EnableMethodSecurity superseded it in Spring Security 5.6. The core change is the authorization engine: the old annotation wired the pre-6 stack — AbstractSecurityInterceptor, AccessDecisionManager, AccessDecisionVoter, and MethodSecurityMetadataSource — whereas the new one uses the streamlined AuthorizationManager API, the same abstraction used for request-based authorization, giving a single consistent model. Practical differences: prePostEnabled now defaults to true (it was false before); customization is via AuthorizationManager beans and interceptor advisors rather than subclassing GlobalMethodSecurityConfiguration; it has first-class support for meta-annotations and templated/parameterized authorization expressions; and it is not tied to AspectJ by default. Migration is usually just swapping the annotation and possibly re-expressing custom voters as AuthorizationManagers. It also aligns method security with the reactive @EnableReactiveMethodSecurity model.
code
java · 17 lines// Before (deprecated)
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // had to opt in explicitly
public class OldConfig extends GlobalMethodSecurityConfiguration {
@Override protected AccessDecisionManager accessDecisionManager() { /* voters */ return null; }
}
// After (current) — prePost already on; customize with an AuthorizationManager bean
@Configuration
@EnableMethodSecurity
public class NewConfig {
@Bean
static AuthorizationManager<MethodInvocation> tenantChecker(TenantService svc) {
return (auth, invocation) ->
new AuthorizationDecision(svc.isInTenant(auth.get()));
}
}go deeper
Just know the new annotation replaced the old deprecated one.
Add that prePostEnabled now defaults to true and the annotation name/import changed.
Explain the AuthorizationManager vs. AccessDecisionManager/voter shift and the new customization path.
Own a migration strategy: re-express custom voters/combining strategies as AuthorizationManagers and audit for behavioral drift.
## Timeline - **`@EnableGlobalMethodSecurity`** — the original method-security switch, present since early Spring Security. **Deprecated** in favor of the new annotation and slated for removal. - **`@EnableMethodSecurity`** — introduced in **Spring Security 5.6**, the recommended replacement; standard in Spring Boot 3 / Spring Security 6. ## What actually changed: the authorization engine The old stack evaluated access through a chain of collaborators: - `MethodSecurityInterceptor` (an `AbstractSecurityInterceptor`) intercepted the call, - consulted a `MethodSecurityMetadataSource` for the annotation's config attributes, - handed them to an **`AccessDecisionManager`** which polled a set of **`AccessDecisionVoter`s** (e.g. `PreInvocationAuthorizationAdviceVoter`, `RoleVoter`) that each returned grant/deny/abstain, combined by a strategy (affirmative/consensus/unanimous). The new stack replaces all of that with the **`AuthorizationManager<T>`** interface — a single functional abstraction (`AuthorizationDecision check(Supplier<Authentication>, T object)`). Method security now registers `AuthorizationManager`-based method interceptors (advisors), the *same* abstraction the `SecurityFilterChain` uses via `authorizeHttpRequests`. This unifies method and web authorization under one mental model and one extension point. ## Concrete behavioral / API differences 1. **`prePostEnabled` default flips to `true`** — you no longer must set it explicitly to get `@PreAuthorize`. 2. **Customization mechanism** — old: subclass `GlobalMethodSecurityConfiguration` and override methods (e.g. `createExpressionHandler`, `accessDecisionManager`). New: publish `AuthorizationManager` beans or your own `Advisor`/method interceptors; register a `MethodSecurityExpressionHandler` bean. 3. **Meta-annotations & templated expressions** — the new engine cleanly supports custom composed annotations (e.g. your own `@IsAdmin`) and parameterized authorization annotations, which were awkward before. 4. **No AspectJ coupling by default** — the new model uses Spring AOP advisors; AspectJ mode is opt-in via `mode = AdviceMode.ASPECTJ`. 5. **Consistent with reactive** — mirrors `@EnableReactiveMethodSecurity`. ## Migration guidance Usually mechanical: replace `@EnableGlobalMethodSecurity(prePostEnabled = true)` with `@EnableMethodSecurity` (prePost already on). If you had custom `AccessDecisionVoter`s or an overridden `GlobalMethodSecurityConfiguration`, re-express that logic as an `AuthorizationManager` bean or a custom SpEL bean referenced from `@PreAuthorize`. Watch for behavior differences if you relied on voter *combining strategies* (e.g. `AffirmativeBased`) — the new model is a straightforward allow/deny per interceptor with defined ordering, not a voter tally. ## Gotcha Both annotations enable AOP-proxy-based enforcement, so the self-invocation limitation is unchanged. Don't expect the migration to fix a `@PreAuthorize` that never fired due to self-invocation or a non-bean.
- What is the single interface that replaced AccessDecisionManager + AccessDecisionVoter in the new model?AuthorizationManager<T>, with its check(Supplier<Authentication>, T) returning an AuthorizationDecision. It's the same abstraction used for authorizeHttpRequests, unifying web and method authorization.
- Does swapping the annotations change the prePostEnabled default?Yes. @EnableGlobalMethodSecurity defaulted prePostEnabled to false, so people set it to true; @EnableMethodSecurity defaults it to true, so a bare annotation already enables @PreAuthorize.
saying these in an interview costs you the question
- Saying they are identical/aliases (the underlying authorization engine differs).
- Claiming @EnableMethodSecurity still uses AccessDecisionManager/voters.
- Thinking migration fixes self-invocation issues (both use AOP proxies).
- Believing prePostEnabled default is the same in both (it flipped to true).