What is the difference between Ant-style and MVC request matchers, and why does Spring recommend MvcRequestMatcher for servlet apps?
answer
- Ant = raw path pattern
- Mvc = same routing as controllers
- trailing-slash / variant bypass
- auto-selects Mvc when DispatcherServlet present
- servlet-path ambiguity → startup error
basics
~10 sAntPathRequestMatcher matches on the raw URL path pattern. MvcRequestMatcher matches using Spring MVC's path-matching, so it understands things like trailing slashes and servlet path mapping the same way your controllers do, avoiding bypass mismatches.
solid answer
~50 sBoth select requests by path, but they parse and match differently. AntPathRequestMatcher does literal Ant pattern matching (?, *, **) against the request URI. MvcRequestMatcher delegates to the same HandlerMappingIntrospector / path-matching that Spring MVC uses to route to controllers, so a security pattern matches exactly what a controller would receive — including trailing-slash handling, suffix/path-parameter quirks, and servlet-context/path-prefix mapping. The danger with pure Ant matching is a mismatch: MVC might route /admin and /admin/ (or /admin.html-style variants) to the same controller, but an Ant rule for /admin only might not cover the variant, creating an authorization bypass. When Spring Security detects a DispatcherServlet, requestMatchers(String) auto-selects MvcRequestMatcher; otherwise it uses AntPathRequestMatcher. When apps have servlet path mappings, you may need requestMatchers with a servlet path or MvcRequestMatcher.Builder to disambiguate, and Spring warns/errors if it is ambiguous.
code
java · 18 lines// Recommended: let requestMatchers(String) auto-pick MvcRequestMatcher in an MVC app
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN") // MvcRequestMatcher: also covers /admin/ etc.
.anyRequest().authenticated());
// Disambiguating when DispatcherServlet is mapped to a non-root path, e.g. /api/*
@Bean
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector).servletPath("/api");
}
@Bean
SecurityFilterChain chain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers(mvc.pattern("/admin/**")).hasRole("ADMIN")
.anyRequest().authenticated());
return http.build();
}go deeper
Just knows requestMatchers takes path patterns with wildcards.
Knows /* wildcards and that method-scoped matchers exist.
Must articulate the MVC-vs-Ant bypass risk and the auto-selection behavior.
Reasons about servlet-path ambiguity, PathPattern migration, and treats matcher/routing alignment as a hardening requirement.
## The two matcher families A **RequestMatcher** decides whether a rule applies to an incoming request. Two common path-based implementations: - **`AntPathRequestMatcher`** — matches the request URI against an **Ant pattern** using wildcards: `?` (one char), `*` (any chars within a path segment), `**` (any number of path segments). It works on the raw path string and knows nothing about how Spring MVC routes requests. - **`MvcRequestMatcher`** — delegates matching to Spring MVC's `HandlerMappingIntrospector`, i.e. the **same path-matching logic (`PathPatternParser` / `AntPathMatcher`) that `DispatcherServlet` uses to map URLs to `@RequestMapping` handlers**. So the security match aligns with the controller match. ## Why the difference matters — the bypass risk Spring MVC is lenient about some URL variants that map to the *same* controller: trailing slashes (`/admin` vs `/admin/`), and historically suffix patterns/path parameters. If you secure `/admin` with a plain Ant matcher but MVC also routes `/admin/` (or another normalized form) to the same handler, an attacker can hit the un-secured variant → **authorization bypass**. `MvcRequestMatcher` closes that gap because it matches requests the way MVC itself resolves them, so `/admin` and its MVC-equivalent variants are all covered by one rule. ## Auto-selection in Spring Security 6 When you call `requestMatchers(String...)`, the DSL picks the implementation for you: - If a `DispatcherServlet` (Spring MVC) is on the classpath/context, it uses **`MvcRequestMatcher`** (recommended default). - Otherwise it falls back to **`AntPathRequestMatcher`**. This is why the modern guidance is: just use `requestMatchers("/path/**")` and let Spring choose — you generally get the safer MVC matcher automatically in MVC apps. ## Servlet path / multiple servlets When the app has a **non-root servlet mapping** (e.g. the DispatcherServlet is mapped to `/api/*`, or there are multiple servlets), pattern matching is ambiguous — Spring Security cannot tell whether your pattern is relative to the servlet path. In that case it **throws at startup** and you must disambiguate: - `requestMatchers("/servletPath").requestMatchers(...)` variants, or - build an `MvcRequestMatcher` via `MvcRequestMatcher.Builder(introspector).servletPath("/api")` and pass it explicitly, or - use `PathPatternRequestMatcher` (newer) / `AntPathRequestMatcher` with the full path. ## When to use which - **Default MVC app** → let `requestMatchers(String)` resolve to `MvcRequestMatcher`. Don't hand-roll `AntPathRequestMatcher` for controller URLs. - **Non-MVC / static resources / custom servlet, or filters before DispatcherServlet** → `AntPathRequestMatcher` (or the auto fallback) is appropriate since there is no MVC routing to align to. - **Multiple/prefixed servlets** → be explicit about the servlet path. ## Gotchas - Don't mix a security pattern that is stricter than MVC routing — the delta is the vulnerability. - `**` in newer `PathPattern` semantics is only allowed at the end of a pattern; the older `AntPathMatcher` was more permissive. Spring Security 6.x has been migrating toward `PathPatternRequestMatcher`. - Regex matching is a separate `RegexRequestMatcher`. - Method + matcher: `requestMatchers(HttpMethod.POST, "/api/**")` also auto-selects the right implementation.
- Give a concrete example of an Ant-matcher authorization bypass.Securing only /admin with AntPathRequestMatcher while Spring MVC also maps /admin/ (trailing slash) to the same @RequestMapping. A request to /admin/ skips the Ant rule but still hits the admin controller. MvcRequestMatcher matches both because it uses MVC's own path resolution.
- When does Spring Security fail at startup over matcher ambiguity, and how do you fix it?When the DispatcherServlet is mapped to a path other than / (or multiple servlets exist), it cannot infer the servlet path for requestMatchers(String). Fix by constructing an MvcRequestMatcher.Builder with .servletPath(...) or otherwise specifying the full/servlet-relative path explicitly.
saying these in an interview costs you the question
- Claiming Ant and Mvc matchers behave identically
- Thinking trailing slashes never matter for security
- Hand-writing AntPathRequestMatcher for controller URLs in an MVC app for no reason