1
0
mirror of synced 2026-08-06 02:08:01 +00:00

Apply Builder Bean Only to Application Endpoints

A published PathPatternRequestMatcher.Builder bean is intended to align
application endpoint matchers, which commonly are from the same servlet.

This commit ensures that this bean is used by securityMatcher(s),
authorizeHttpRequests, and WebSecurity#ignoring, which target
application endpoints typically served by servlets.

This commit also adds tests ensuring that the bean is not applied to filter-chain
security endpoints such as those configured by formLogin, logout,
oneTimeTokenLogin, OAuth 2.0 Login, and SAML 2.0 Login, since
these endpoints are, by default, handled by filters independent from
any target servlet and servlet path.

Closes gh-19128

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
This commit is contained in:
Josh Cummings
2026-05-22 21:36:19 -06:00
parent f70aa75445
commit cfdadfea01
18 changed files with 517 additions and 4 deletions
@@ -81,6 +81,12 @@ Xml::
and no authorization changes are necessary since it simply adjusts the `LogoutFilter`.
[NOTE]
====
The URI passed to `logoutUrl` (and to `logoutSuccessUrl`) is matched and redirected to literally.
If you want either URI to live under your application servlet's base path, include that prefix explicitly — for example, `logoutUrl("/api/logout")`.
====
[[permit-logout-endpoints]]
However, if you stand up your own logout success endpoint (or in a rare case, <<creating-custom-logout-endpoint, your own logout endpoint>>), say using {spring-framework-reference-url}web.html#spring-web[Spring MVC], you will need to permit it in Spring Security.
This is because Spring MVC processes your request after Spring Security does.
@@ -252,6 +252,13 @@ class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSucc
----
======
[NOTE]
====
The URI passed to `generateTokenUrl` is matched literally.
If you want it to live under your application servlet's base path, include that prefix explicitly — for example, `generateTokenUrl("/api/ott/generate")`.
The same applies to `loginProcessingUrl` and `tokenGeneratingUrl` elsewhere on this page.
====
[[changing-submit-page-url]]
== Changing the Default Submit Page URL
@@ -155,6 +155,15 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
----
======
[NOTE]
====
The URI passed to `loginPage` (and to `loginProcessingUrl`, if set separately) is matched and redirected to literally.
If your custom login page is served by an application servlet mapped under a base path, include that prefix in the URI you pass to the DSL.
For example, if your dispatcher is at `/api/*`:
include-code::./FormLoginServletPathConfiguration[tag=loginPage,indent=0]
====
[[servlet-authentication-form-custom-html]]
When the login page is specified in the Spring Security configuration, you are responsible for rendering the page.
// FIXME: default login page rendered by Spring Security
@@ -642,6 +642,8 @@ This is because Spring Security requires all URIs to be absolute (minus the cont
There are several other components that create request matchers for you like {spring-boot-api-url}org/springframework/boot/security/autoconfigure/web/servlet/PathRequest.html[`PathRequest#toStaticResources#atCommonLocations`]
=====
If most of your authorization rules sit under the same servlet path, you can xref:servlet/integrations/mvc.adoc#mvc-requestmatcher[publish a `PathPatternRequestMatcher.Builder` bean] with that base path; Spring Security then applies it to the string overloads of `authorizeHttpRequests((authorize) -> authorize.requestMatchers(...))`, `HttpSecurity#securityMatcher(...)` / `#securityMatchers(...)`, and `WebSecurityCustomizer#ignoring().requestMatchers(...)`.
[[match-by-custom]]
=== Using a Custom Matcher
@@ -0,0 +1,46 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.passwords.formloginservletpath;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class FormLoginServletPathConfiguration {
// tag::loginPage[]
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/api/login")
.loginProcessingUrl("/api/login")
);
// @formatter:on
return http.build();
}
// end::loginPage[]
}
@@ -0,0 +1,32 @@
package org.springframework.security.kt.docs.servlet.authentication.passwords.formloginservletpath
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
class FormLoginServletPathConfiguration {
// tag::loginPage[]
@Bean
fun springSecurity(http: HttpSecurity): SecurityFilterChain {
// @formatter:off
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
formLogin {
loginPage = "/api/login"
loginProcessingUrl = "/api/login"
}
}
return http.build()
// @formatter:on
}
// end::loginPage[]
}