Problem Description

The current interaction between URL-based security (requestMatchers()) and method-level annotations (@PermitAll, @PreAuthorize) creates confusion regarding precedence rules. Specifically:

  1. Unexpected Behavior:
    When a path is secured via requestMatchers("/api/**").authenticated(), adding @PermitAll on a controller method does not override the URL-level restriction, contrary to what many developers expect .

  2. Documentation Gap:
    The official documentation doesn't clearly state that requestMatchers() takes precedence over method annotations .

Proposed Solutions

Option 1: Behavior Change (Breaking Change)

Make method-level annotations override URL configurations when:

  • Add new @OverrideSecurityConfig meta-annotation

  • Or introduce explicit configuration:

java @Configuration @EnableMethodSecurity(overrideUrlSecurity = true) // New flag

Option 2: Documentation Enhancement

Clearly document the current precedence rules with warning notes:

**Security Configuration Precedence**:
1. URL patterns (`requestMatchers()`) 
2. Method annotations (`@PreAuthorize`, `@PermitAll`)

Important: Method annotations cannot override URL-level `authenticated()`/`denyAll()`. 
Use `@PermitAll` only when the URL config is `permitAll()`.

Option 3: Hybrid Approach

Introduce a new annotation @SecurityOverride that explicitly declares intent:

@PermitAll
@SecurityOverride // Explicitly declares overriding URL config
@GetMapping("/api/public")
public String publicApi() { ... }

Why This Matters

  1. Developer Experience:
    Most developers expect method annotations to naturally override coarser URL configurations .

  2. Security Pitfalls:
    The current behavior may lead to accidental exposure of endpoints when developers rely on @PermitAll that doesn't actually work .

  3. Consistency:
    Other security frameworks (e.g., Apache Shiro) allow method-level rules to override path-based rules.

Current Workaround

The only way to achieve method-level overriding today is through custom implementation:

@Component
public class CustomAuthManager implements AuthorizationManager<MethodInvocation> {
    @Override
    public AuthorizationDecision check(Supplier<Authentication> auth, MethodInvocation method) {
        // Custom logic to check annotations first
    }
}

This is unnecessarily complex for a common use case .

Evidence of Confusion