Search code examples
javaspringspring-bootspring-security

Disable Spring's method security for unit tests with Spring Boot 3


I am using @PreAuthorize on methods in service interfaces. Theses services are used by @RestController classes

With Spring Boot 2.x I enabled method security in a AutoConfiguration with @EnableGlobalMethodSecurity(prePostEnabled = true)

In some @SpringBootTests we concentrate on testing the functional logic and don't care about method security. Theses tests do no call a rest service or controller class. They directly call method from above mentioned service classes which are annotated with @PreAuthorize. In these tests we disabled the method security by

@EnableAspectJAutoProxy
class MyTestApplication extends MySringBootApplication {

    @Bean
    public GeFaJwtInterceptorTestAspect jwtInterceptorTestAspect() {
        return new GeFaJwtInterceptorTestAspect();
    }

    @Bean
    @Primary
    SecurityMetadataSource disableMethodSecurityForUnitTest() {
        return new DefaultFilterInvocationSecurityMetadataSource(Maps.newLinkedHashMap());
    }
}

That works fine.

After migration to Spring Boot 3 I am using @EnableMethodSecurity now.

How can I disable Spring's security method in unit tests?

I read the article https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html#enable-annotation but did not find a solution there.

I tried out

  • using @EnableMethodSecurity(prePostEnabled = false) at MyTestApplication
  • adding a bean in my test configuration
      @Bean
      @Primary
      @Order(Ordered.HIGHEST_PRECEDENCE)
      AuthorizationManager<MethodInvocation> disableMethodSecurityForUnitTest1() {
          return (authentication, object) -> new AuthorizationDecision(true);
      }
    

But that did not work. Each try end up with one of the following exceptions

org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext

org.springframework.security.access.AccessDeniedException: Access Denied

Currently my only fix is to add @WithMockUser on each test method which is not what I want as I want to test the functional logic only. (Method security is tested, too, but on another layer in our application.)


Solution

  • Thank's to Mar-Z answer I came to the clue to disable it by excluding the AutoConfiguration which activates the MethodSecurity.

    Currently I only had one AutoConfigration in my maven module. That holds a couple of different Beans/ aspects. So at first I had to separate that AutoConfiguration into two parts. A second AutoConfiguration which only holds the configuration for activating the method security As exclude does only works for AutoConfiguration it must be an AutoConfiguration - Configuration only does not work

    
    @AutoConfiguration
    @EnableMethodSecurity
    public class MyServiceSecurityAutoConfiguration {
        // you could add any Beans here relevant for Authorization / method security etc.
    }
    

    Of course the class has to be added to src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

    In my SpringBootTest I excluded this AutoConfiguration like this

    @ExtendWith(SpringExtension.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 
              classes = {MyIntegrationTestApplication.class})
    @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
    @ActiveProfiles({"dev", "console", "test"})
    public class MyIntegrationTest {
    
        // any tests here
    
    }
    
    @EnableAutoConfiguration(exclude = {MyServiceSecurityAutoConfiguration .class})
    class MyIntegrationTestApplication extends MySpringBootApplication {
    
        // Maybe you need to define some mandatory beans here if they are not loaded by AutoConfiguration
    }
    

    If you do not need to extend the SpringBootApplication (here MySpringBootApplication) by any additional bean you can also do this to exclude the MethodeSecurity-Configuration in your test:

    @SpringBootTest(classes = {MySpringBootApplication .class})
    @EnableAutoConfiguration(exclude = {MyServiceSecurityAutoConfiguration .class})
    class MyIntegrationTest {
    
        // any tests here
    
    }
    

    May be that helps other members, too.