= Configuration Migrations The following steps relate to changes around how to configure `HttpSecurity`, `WebSecurity`, and `AuthenticationManager`. [[use-new-requestmatchers]] == Use the new `requestMatchers` methods In Spring Security 5.8, the {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#antMatchers(java.lang.String...)[`antMatchers`], {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#mvcMatchers(java.lang.String...)[`mvcMatchers`], and {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#regexMatchers(java.lang.String...)[`regexMatchers`] methods were deprecated in favor of new xref:servlet/authorization/authorize-http-requests.adoc#_request_matchers[`requestMatchers` methods]. The new `requestMatchers` methods were added xref:servlet/authorization/authorize-http-requests.adoc[to `authorizeHttpRequests`], `authorizeRequests`, CSRF configuration, `WebSecurityCustomizer` and any other places that had the specialized `RequestMatcher` methods. The deprecated methods are removed in Spring Security 6. These new methods have more secure defaults since they choose the most appropriate `RequestMatcher` implementation for your application. In summary, the new methods choose the `MvcRequestMatcher` implementation if your application has Spring MVC in the classpath, falling back to the `AntPathRequestMatcher` implementation if Spring MVC is not present (aligning the behavior with the Kotlin equivalent methods). To start using the new methods, you can replace the deprecated methods with the new ones. For example, the following application configuration: ==== .Java [source,java,role="primary"] ---- @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .antMatchers("/api/admin/**").hasRole("ADMIN") .antMatchers("/api/user/**").hasRole("USER") .anyRequest().authenticated() ); return http.build(); } } ---- ==== can be changed to: ==== .Java [source,java,role="primary"] ---- @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .requestMatchers("/api/admin/**").hasRole("ADMIN") .requestMatchers("/api/user/**").hasRole("USER") .anyRequest().authenticated() ); return http.build(); } } ---- ==== If you have Spring MVC in the classpath and are using the `mvcMatchers` methods, you can replace it with the new methods and Spring Security will choose the `MvcRequestMatcher` implementation for you. The following configuration: ==== .Java [source,java,role="primary"] ---- @Configuration @EnableWebSecurity @EnableWebMvc public class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .mvcMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } } ---- ==== is equivalent to: ==== .Java [source,java,role="primary"] ---- @Configuration @EnableWebSecurity @EnableWebMvc public class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } } ---- ==== If you are customizing the `servletPath` property of the `MvcRequestMatcher`, you can now use the `MvcRequestMatcher.Builder` to create `MvcRequestMatcher` instances that share the same servlet path: ==== .Java [source,java,role="primary"] ---- @Configuration @EnableWebSecurity @EnableWebMvc public class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .mvcMatchers("/admin").servletPath("/path").hasRole("ADMIN") .mvcMatchers("/user").servletPath("/path").hasRole("USER") .anyRequest().authenticated() ); return http.build(); } } ---- ==== The code above can be rewritten using the `MvcRequestMatcher.Builder` and the `requestMatchers` method: ==== .Java [source,java,role="primary"] ---- @Configuration @EnableWebSecurity @EnableWebMvc public class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path"); http .authorizeHttpRequests((authz) -> authz .requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN") .requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER") .anyRequest().authenticated() ); return http.build(); } } ---- ==== If you are having problem with the new `requestMatchers` methods, you can always switch back to the `RequestMatcher` implementation that you were using. For example, if you still want to use `AntPathRequestMatcher` and `RegexRequestMatcher` implementations, you can use the `requestMatchers` method that accepts a `RequestMatcher` instance: ==== .Java [source,java,role="primary"] ---- import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; import static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .requestMatchers(antMatcher("/user/**")).hasRole("USER") .requestMatchers(antMatcher(HttpMethod.POST, "/user/**")).hasRole("ADMIN") .requestMatchers(regexMatcher(".*\\?x=y")).hasRole("SPECIAL") // matches /any/path?x=y .anyRequest().authenticated() ); return http.build(); } } ---- ==== Note that the above sample uses static factory methods from {security-api-url}org/springframework/security/web/util/matcher/AntPathRequestMatcher.html[`AntPathRequestMatcher`] and {security-api-url}org/springframework/security/web/util/matcher/RegexRequestMatcher.html[`RegexRequestMatcher`] to improve readability. If you are using the `WebSecurityCustomizer` interface, you can replace the deprecated `antMatchers` methods: ==== .Java [source,java,role="primary"] ---- @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2"); } ---- ==== with their `requestMatchers` counterparts: ==== .Java [source,java,role="primary"] ---- @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().requestMatchers("/ignore1", "/ignore2"); } ---- ==== The same way, if you are customizing the CSRF configuration to ignore some paths, you can replace the deprecated methods with the `requestMatchers` methods: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf((csrf) -> csrf .ignoringAntMatchers("/no-csrf") ); return http.build(); } ---- ==== can be changed to: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf((csrf) -> csrf .ignoringRequestMatchers("/no-csrf") ); return http.build(); } ---- ==== [[use-new-security-matchers]] == Use the new `securityMatchers` methods In Spring Security 5.8, the `antMatchers`, `mvcMatchers` and `requestMatchers` methods from `HttpSecurity` were deprecated in favor of new `securityMatchers` methods. Note that these methods are not the same from `authorizeHttpRequests` methods <> in favor of the `requestMatchers` methods. However, the `securityMatchers` methods are similar to the `requestMatchers` methods in the sense that they will choose the most appropriate `RequestMatcher` implementation for your application. In summary, the new methods choose the `MvcRequestMatcher` implementation if your application has Spring MVC in the classpath, falling back to the `AntPathRequestMatcher` implementation if Spring MVC is not present (aligning the behavior with the Kotlin equivalent methods). Another reason for adding the `securityMatchers` methods is to avoid confusion with the `requestMatchers` methods from `authorizeHttpRequests`. The following configuration: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .antMatcher("/api/**", "/app/**") .authorizeHttpRequests((authz) -> authz .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } ---- ==== can be rewritten using the `securityMatchers` methods: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .securityMatcher("/api/**", "/app/**") .authorizeHttpRequests((authz) -> authz .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } ---- ==== If you are using a custom `RequestMatcher` in your `HttpSecurity` configuration: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .requestMatcher(new MyCustomRequestMatcher()) .authorizeHttpRequests((authz) -> authz .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } public class MyCustomRequestMatcher implements RequestMatcher { // ... } ---- ==== you can do the same using `securityMatcher`: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .securityMatcher(new MyCustomRequestMatcher()) .authorizeHttpRequests((authz) -> authz .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } public class MyCustomRequestMatcher implements RequestMatcher { // ... } ---- ==== If you are combining multiple `RequestMatcher` implementations in your `HttpSecurity` configuration: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .requestMatchers((matchers) -> matchers .antMatchers("/api/**", "/app/**") .mvcMatchers("/admin/**") .requestMatchers(new MyCustomRequestMatcher()) ) .authorizeHttpRequests((authz) -> authz .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } ---- ==== you can change it by using `securityMatchers`: ==== .Java [source,java,role="primary"] ---- @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .securityMatchers((matchers) -> matchers .requestMatchers("/api/**", "/app/**", "/admin/**") .requestMatchers(new MyCustomRequestMatcher()) ) .authorizeHttpRequests((authz) -> authz .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } ---- ==== If you are having problems with the `securityMatchers` methods choosing the `RequestMatcher` implementation for you, you can always choose the `RequestMatcher` implementation yourself: ==== .Java [source,java,role="primary"] ---- import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .securityMatchers((matchers) -> matchers .requestMatchers(antMatcher("/api/**"), antMatcher("/app/**")) ) .authorizeHttpRequests((authz) -> authz .requestMatchers(antMatcher("/api/admin/**")).hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } ---- ==== == Stop Using `WebSecurityConfigurerAdapter` === Publish a `SecurityFilterChain` Bean Spring Security 5.4 introduced the capability to publish a `SecurityFilterChain` bean instead of extending `WebSecurityConfigurerAdapter`. In 6.0, `WebSecurityConfigurerAdapter` is removed. To prepare for this change, you can replace constructs like: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated() ) .httpBasic(withDefaults()); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration: WebSecurityConfigurerAdapter() { @Override override fun configure(val http: HttpSecurity) { http { authorizeHttpRequests { authorize(anyRequest, authenticated) } httpBasic {} } } } ---- ==== with: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated() ) .httpBasic(withDefaults()); return http.build(); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration { @Bean fun filterChain(http: HttpSecurity): SecurityFilterChain { http { authorizeHttpRequests { authorize(anyRequest, authenticated) } httpBasic {} } return http.build() } } ---- ==== === Publish a `WebSecurityCustomizer` Bean Spring Security 5.4 https://github.com/spring-projects/spring-security/issues/8978[introduced `WebSecurityCustomizer`] to replace `configure(WebSecurity web)` in `WebSecurityConfigurerAdapter`. To prepare for its removal, you can replace code like the following: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) { web.ignoring().antMatchers("/ignore1", "/ignore2"); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration: WebSecurityConfigurerAdapter() { override fun configure(val web: WebSecurity) { web.ignoring().antMatchers("/ignore1", "/ignore2") } } ---- ==== with: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration { @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2"); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration { @Bean fun webSecurityCustomizer(): WebSecurityCustomizer { return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2") } } ---- ==== === Publish an `AuthenticationManager` Bean As part of `WebSecurityConfigurerAdapeter` removal, `configure(AuthenticationManagerBuilder)` is also removed. Preparing for its removal will differ based on your reason for using it. ==== LDAP Authentication If you are using `auth.ldapAuthentication()` for xref:servlet/authentication/passwords/ldap.adoc[LDAP authentication support], you can replace: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .ldapAuthentication() .userDetailsContextMapper(new PersonContextMapper()) .userDnPatterns("uid={0},ou=people") .contextSource() .port(0); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration: WebSecurityConfigurerAdapter() { override fun configure(auth: AuthenticationManagerBuilder) { auth .ldapAuthentication() .userDetailsContextMapper(PersonContextMapper()) .userDnPatterns("uid={0},ou=people") .contextSource() .port(0) } } ---- ==== with: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration { @Bean public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean = EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer(); contextSourceFactoryBean.setPort(0); return contextSourceFactoryBean; } @Bean AuthenticationManager ldapAuthenticationManager(BaseLdapPathContextSource contextSource) { LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); factory.setUserDnPatterns("uid={0},ou=people"); factory.setUserDetailsContextMapper(new PersonContextMapper()); return factory.createAuthenticationManager(); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration { @Bean fun contextSourceFactoryBean(): EmbeddedLdapServerContextSourceFactoryBean { val contextSourceFactoryBean: EmbeddedLdapServerContextSourceFactoryBean = EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer() contextSourceFactoryBean.setPort(0) return contextSourceFactoryBean } @Bean fun ldapAuthenticationManager(val contextSource: BaseLdapPathContextSource): AuthenticationManager { val factory = LdapBindAuthenticationManagerFactory(contextSource) factory.setUserDnPatterns("uid={0},ou=people") factory.setUserDetailsContextMapper(PersonContextMapper()) return factory.createAuthenticationManager() } } ---- ==== ==== JDBC Authentication If you are using `auth.jdbcAuthentication()` for xref:servlet/authentication/passwords/jdbc.adoc[JDBC Authentication support], you can replace: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .build(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build(); auth.jdbcAuthentication() .withDefaultSchema() .dataSource(this.dataSource) .withUser(user); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration: WebSecurityConfigurerAdapter() { @Bean fun dataSource(): DataSource { return EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .build() } override fun configure(val auth: AuthenticationManagerBuilder) { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build() auth.jdbcAuthentication() .withDefaultSchema() .dataSource(this.dataSource) .withUser(user) } } ---- ==== with: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) .build(); } @Bean public UserDetailsManager users(DataSource dataSource) { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build(); JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource); users.createUser(user); return users; } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration { @Bean fun dataSource(): DataSource { return EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) .build() } @Bean fun users(val dataSource: DataSource): UserDetailsManager { val user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build() val users = JdbcUserDetailsManager(dataSource) users.createUser(user) return users } } ---- ==== ==== In-Memory Authentication If you are using `auth.inMemoryAuthentication()` for xref:servlet/authentication/passwords/in-memory.adoc[In-Memory Authentication support], you can replace: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build(); auth.inMemoryAuthentication() .withUser(user); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration: WebSecurityConfigurerAdapter() { override fun configure(val auth: AuthenticationManagerBuilder) { val user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build() auth.inMemoryAuthentication() .withUser(user) } } ---- ==== with: ==== .Java [source,java,role="primary"] ---- @Configuration public class SecurityConfiguration { @Bean public InMemoryUserDetailsManager userDetailsService() { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Configuration open class SecurityConfiguration { @Bean fun userDetailsService(): InMemoryUserDetailsManager { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build() return InMemoryUserDetailsManager(user) } } ---- ==== ==== Other Scenarios If you are using `AuthenticationManagerBuilder` for something more sophisticated, you can xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[publish your own `AuthenticationManager` `@Bean`] or wire an `AuthenticationManager` instance into the `HttpSecurity` DSL with {security-api-url}org/springframework/security/config/annotation/web/builders/HttpSecurity.html#authenticationManager(org.springframework.security.authentication.AuthenticationManager)[`HttpSecurity#authenticationManager`].