What RequestMatcher does a SecurityFilterChain use if you never call securityMatcher, and why does that matter?
answer
- no securityMatcher => AnyRequestMatcher (matches all)
- fine for single chain
- catch-all must be ordered last
- securityMatcher != anyRequest()
- two default chains = bug
basics
~10 sIt uses AnyRequestMatcher, so the chain matches every request. That makes it a catch-all, which must be ordered last or it will shadow more specific chains.
solid answer
~40 sIf you never call http.securityMatcher(...), the resulting SecurityFilterChain is built with AnyRequestMatcher.INSTANCE, meaning matches(request) always returns true — the chain applies to every request. That's the intended behavior for a single-chain app (one chain secures everything). But with multiple chains it makes this chain a catch-all: since FilterChainProxy uses first-match, an AnyRequestMatcher chain placed before a specific /api/** chain will match /api/** itself and prevent the specific chain from ever running. So an unmatched (default) chain must be ordered last. Note the distinction: securityMatcher sets this chain-selection matcher; it's different from anyRequest() inside authorizeHttpRequests, which is an authorization rule within an already-selected chain.
code
java · 15 lines// No securityMatcher -> AnyRequestMatcher -> matches every request (catch-all).
@Bean @Order(2)
SecurityFilterChain catchAll(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a.anyRequest().authenticated()) // authZ rule, NOT chain selection
.formLogin(Customizer.withDefaults());
return http.build();
}
// With securityMatcher -> only /actuator/** selects this chain.
@Bean @Order(1)
SecurityFilterChain actuator(HttpSecurity http) throws Exception {
http.securityMatcher("/actuator/**")
.authorizeHttpRequests(a -> a.anyRequest().hasRole("ADMIN"));
return http.build();
}go deeper
Know that with no securityMatcher a chain secures every request.
Explain AnyRequestMatcher default and why the catch-all must be last.
Distinguish the three matcher layers and diagnose shadowing from logs.
Treat catch-all-last as an architectural invariant across many chains.
## The default matcher `HttpSecurity.securityMatcher(...)` sets the `RequestMatcher` that `FilterChainProxy` uses to decide whether the built `SecurityFilterChain` applies. If you **never call it**, `http.build()` produces a `DefaultSecurityFilterChain` whose matcher is `AnyRequestMatcher.INSTANCE`. `AnyRequestMatcher.matches(...)` always returns `true`, so the chain matches **every** request. ### Why this is fine for one chain Most simple apps declare a single `SecurityFilterChain`. With one chain, you *want* it to secure everything, so the `AnyRequestMatcher` default is exactly right and you never call `securityMatcher`. ### Why it's dangerous with multiple chains `FilterChainProxy` selects the **first** matching chain. A chain that matches everything is a catch-all. If ordered before a more specific chain, it wins for all paths and the specific chain becomes dead code. Therefore: - Chains **with** a `securityMatcher` (specific) go first, ordered most-specific-first. - The chain **without** a `securityMatcher` (catch-all) goes **last**. ### The three easily-confused matchers 1. `securityMatcher(...)` — selects whether the whole chain applies (chain selection). Default: `AnyRequestMatcher`. 2. `authorizeHttpRequests(a -> a.requestMatchers(...))` — authorization rules for endpoints *within* a selected chain. 3. `authorizeHttpRequests(a -> a.anyRequest()...)` — the fallback authorization rule inside a selected chain (NOT chain selection). A common mistake is thinking `anyRequest().authenticated()` controls which chain runs — it does not; it controls access *after* the chain is chosen. ### Gotchas - Two chains both defaulting to `AnyRequestMatcher` (both missing `securityMatcher`) is almost always a bug: the earlier one wins for everything. - Startup logs print each chain's matcher; a chain listed as `any request` is your catch-all — confirm it's last. - `securityMatcher` accepts patterns (`/api/**`), a `RequestMatcher`, or HTTP-method-scoped matchers, letting you scope a chain by path and/or method.
- Is anyRequest() inside authorizeHttpRequests the same as not calling securityMatcher?No. anyRequest() is an authorization rule applied after a chain is selected. Not calling securityMatcher sets the chain's selection matcher to AnyRequestMatcher, deciding whether the chain applies at all. Different layers: chain selection vs access control.
- What's the symptom of two SecurityFilterChain beans that both omit securityMatcher?Both default to AnyRequestMatcher, so the lower-@Order one wins for every request and the other never runs — its configuration silently has no effect.
saying these in an interview costs you the question
- Thinking a chain without securityMatcher matches nothing (it matches everything).
- Placing the default/catch-all chain before specific chains.
- Conflating securityMatcher (chain selection) with anyRequest() (authorization).