37

I'm trying to upgrade to Spring Boot 3.0.0 and Spring Security 6.0.

I've found that method for securing requests authorizeRequests() has been deprecated. And also method antMatchers() and @EnableGlobalMethodSecurity annotation has removed. How can I upgrade my security configuration?

My code:

package org.sid.securityservice.config;

import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    private RsakeysConfig rsakeysConfig;
    private PasswordEncoder passwordEncoder;

    public SecurityConfig(RsakeysConfig rsakeysConfig, PasswordEncoder passwordEncoder) {
        this.rsakeysConfig = rsakeysConfig;
        this.passwordEncoder = passwordEncoder;
    }

    //@Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
    @Bean
    public AuthenticationManager authenticationManager(UserDetailsService userDetailsService){
        var authProvider = new DaoAuthenticationProvider();
        authProvider.setPasswordEncoder(passwordEncoder);
        authProvider.setUserDetailsService(userDetailsService);
        return new ProviderManager(authProvider);
    }

    @Bean
    public UserDetailsService inMemoryUserDetailsManager(){
        return new InMemoryUserDetailsManager(
                User.withUsername("user1").password(passwordEncoder.encode("1234")).authorities("USER").build(),
                User.withUsername("user2").password(passwordEncoder.encode("1234")).authorities("USER").build(),
                User.withUsername("admin").password(passwordEncoder.encode("1234")).authorities("USER","ADMIN").build()
        );
    }
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf->csrf.disable())
                .authorizeRequests(auth->auth.antMatchers("/token/**").permitAll())
                .authorizeRequests(auth->auth.anyRequest().authenticated())
                .sessionManagement(sess->sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .httpBasic(Customizer.withDefaults())
                .build();
    }
    @Bean
    JwtDecoder jwtDecoder(){
        return NimbusJwtDecoder.withPublicKey(rsakeysConfig.publicKey()).build();
    }
    @Bean
    JwtEncoder jwtEncoder(){
        JWK jwk= new RSAKey.Builder(rsakeysConfig.publicKey()).privateKey(rsakeysConfig.privateKey()).build();
        JWKSource<SecurityContext> jwkSource= new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwkSource);
    }

}

Here's what IDE shows me (struck out authorizeRequests() and missing antMatchers() highlighted in red):

enter image description here

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
Vy Do
  • 46,709
  • 59
  • 215
  • 313

1 Answers1

80

In Spring Security 6.0, antMatchers() as well as other configuration methods for securing requests (namely mvcMatchers() and regexMatchers()) have been removed from the API.

An overloaded method requestMatchers() was introduced as a uniform mean for securing requests. The flavors of requestMatchers() facilitate all the ways of restricting requests that were supported by the removed methods.

Also, method authorizeRequests() has been deprecated and shouldn't be used anymore. A recommended replacement - authorizeHttpRequests() (you can find more information regarding these changes here).

That's how your SecurityFilterChain might be defined in Spring Security 6.0:

public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
    return httpSecurity
        .csrf(csrf -> csrf.disable())
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/token/**").permitAll()
            .anyRequest().authenticated()
        )
        .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
        .httpBasic(Customizer.withDefaults())
        .build();
}

Regarding deprecated annotation @EnableGlobalMethodSecurity it was replaced with @EnableMethodSecurity. The rationale behind this change is that with @EnableMethodSecurity property prePostEnabled needed to enable use of @PreAuthorize/@PostAuthorize and @PreFilter/@PostFilter is by default set to true.

So you no longer need to write prePostEnabled = true, just annotating your configuration class with @EnableMethodSecurity would be enough.

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • @JamesGrey `conr()` is not present in the configuration you've posted as well. I'm not aware of the allowed origins and headers in your application. – Alexander Ivanchenko Dec 05 '22 at 13:29
  • ok, I checked. I can control cors() success. – Vy Do Dec 05 '22 at 13:39
  • @JamesGrey Ok, if you would need information on configuring CORS, check this [link](https://docs.spring.io/spring-security/reference/reactive/integrations/cors.html). – Alexander Ivanchenko Dec 05 '22 at 13:41
  • 3
    @AlexanderIvanchenko - This line seems not to work though -> `.requestMatchers("/token/**").permitAll()`. When upgraded to Spring 6.0, the `/token/**` path requires authentication. Is there anything else that needs to be added to allow the `permitAll`? Thanks! – securecurve Jan 09 '23 at 10:56
  • @securecurve You can't apply security roles created via `requestMatchers()` in isolation (and there's nothing wrong `requestMatchers(...).permitAll()`. There's something else in your security configuration that causes the problem. In order to tell what exactly, you have to provide a sufficient problem description, firstly you need to show the **current security configuration** and describe the **failing behavior** (preferably in the form of [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)). I would advise opening a new question for that. – Alexander Ivanchenko Jan 11 '23 at 13:02
  • @securecurve, I have the same problem, maybe you should also watch my question in case anyone answers.... https://stackoverflow.com/questions/75080739/spring-security-6-post-requests-are-unauthorised-with-permitall – Octavia Jan 11 '23 at 15:30
  • @OctaviaAdler Your problem has nothing to do with `requestMatchers()`. Learn about [**CSRF-protection**](https://docs.spring.io/spring-security/reference/features/exploits/csrf.html) mechanism, which guards the state of the application against undesired changes (therefore only mutating HTTP-methods `POST`, `PATCH`, etc. would trigger CSRF-protection, but `GET` which is supposed to be idempotent wouldn't). CSRF-protection is enabled by **default** in Spring Security. Usually, when an application is intended to be used only by *non-browser clients*, you want to disable CSRF-protection. – Alexander Ivanchenko Jan 12 '23 at 00:23
  • @AlexanderIvanchenko, thanks for your replay, yes, it has to do with CSRF protection, but I send the CSRF token, so yeah, I still don't understand why it doesn't work – Octavia Jan 12 '23 at 07:24
  • @OctaviaAdler Thanks for the reference to your question. After several tests, there was nothing wrong with `requestMatchers`- Thanks, @Alexander! – securecurve Jan 15 '23 at 11:14
  • The samples mention in spring docs https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html still use `antMatcher` – Alireza Fattahi Feb 01 '23 at 15:42
  • @AlirezaFattahi You're confusing [`AntPathRequestMatcher.antMatcher()`](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/util/matcher/AntPathRequestMatcher.html#antMatcher(java.lang.String)) (an overloaded static method added with Spring Security 5.8) and `antMatchers()` (*have a careful look at the link you've referenced*). `AntPathRequestMatcher` is one of the implementations of `RequestMatcher` interface provided by Spring Security (*there are few more like* `RegexRequestMatcher`). – Alexander Ivanchenko Feb 01 '23 at 22:37
  • @AlirezaFattahi `RequestMatcher` instance can be used while defining security rules with [`requestMatchers()`](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#requestMatchers(org.springframework.security.web.util.matcher.RequestMatcher...)) and [`HttpSecurity.securityMatcher()`](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/builders/HttpSecurity.html#securityMatcher(org.springframework.security.web.util.matcher.RequestMatcher)). – Alexander Ivanchenko Feb 01 '23 at 22:40
  • @AlexanderIvanchenko Maybe you need to declare any permitAll requestMatcher before authenticated or fullyAuthenticated matchers. – Jamali Feb 02 '23 at 14:52
  • @Jamali That what exactly what the very first specified authorization rule does `requestMatchers("/token/**").permitAll()`. – Alexander Ivanchenko Feb 03 '23 at 02:57