@EnableMethodSecurity activates method interceptors, but an internal call on this does not pass through the proxy.
Spring method security: authorization advice runs through the bean proxy
The controller is not the only entry point
A stock-adjust service may be called from a controller, scheduled job, message listener or another service. A request matcher cannot guard all four. @PreAuthorize on the service method can enforce a caller authority at that boundary after @EnableMethodSecurity is configured. The annotation does not secure every unannotated method automatically, so keep a catch-all web rule and review service entry points.
Avoid self-invocation
If an unprotected method on the same object calls this.adjustStock(), that call bypasses proxy advice. Move the protected operation into a separate injected Spring bean, or enforce the decision explicitly through an AuthorizationManager. The proxy boundary also affects transactions. Tenant claims must be compared with the method argument, not merely checked for presence; tenant validation remains a separate rule.
Test a non-web caller
Call the service through a Spring-injected bean with a principal lacking the authority, and assert AccessDeniedException before the repository writes. Then invoke it through a message handler and verify the same rule. A MockMvc-only test may miss a self-invocation path that never enters the method interceptor.
Implementation sketch
@EnableMethodSecurity
@Configuration
class MethodAuthorizationConfig {}
@Service
class StockAdjustmentService {
@PreAuthorize("hasAuthority('SCOPE_stock.write')")
public void adjust(String tenantId, StockChange change) {
stockLedger.apply(tenantId, change);
}
}Cost and verification
Method authorization adds an interceptor and expression evaluation per guarded call. A database authorization lookup inside every expression can dominate that cost and should be measured.
Common Mistakes
- Do not assume @PreAuthorize runs on an internal this.method() call.
- Do not expect @EnableMethodSecurity to protect unannotated methods.
- Do not check only a broad scope when the method also accepts a tenant ID.
Read next
Spring AOP proxies: self-invocation bypasses proxy advice, Spring Security filter chain: authentication, CSRF and request order, Spring JWT tenant claims: reject missing or malformed ownership before conversion, Spring @PostAuthorize: denial happens after the method has run.
Related boundary
Spring Security SAML attributes: map identity to local authority
