Disable Spring Security config class for @WebMvcTest in Spring Boot
Asked Answered
S

12

45

Recently I have added Spring Security to my Spring Boot project using the following class:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig {
}

as result, by default all my URLs are now protected with authentication and a self-generated password.

The problem is that all tests in a @WebMvcTest class that I used for unit-testing a controller:

@RunWith(SpringRunner.class)
@WebMvcTest(SomeController.class)
public class SomeControllerTest {...}

are now failing everywhere because of lack of authorization.

Question: can I tell @Test methods to ignore authorization so they keep succeeding as before?

How can I prevent the @EnableWebSecurity config class from being picked on a specific @WebMvcTest unit testing class?

I would like the tests already in place to be able to still go through and to test the authentication features separately later on.

So far I have tried to use a nested config class in the testing class in order to exclude security configs:

@RunWith(SpringRunner.class)
@WebMvcTest(SomeController.class)
public class SomeControllerTest {

    @Configuration
    @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class})
    static class ContextConfiguration { }

 ....}

but it seems not to work.

NOTE : I am using Spring Boot 1.5.8

Staphylorrhaphy answered 1/12, 2017 at 12:35 Comment(1)
docs.spring.io/spring-security/site/docs/current/reference/html/… or another way is to use another security config for tests, use Profile and ActiveProfiles annotations for itPuma
L
21

You can set secure=false in the @WebMvcTest annoation. It will skip the spring security MockMvc auto configuration in your Test

@WebMvcTest(controllers = SomeController.class, secure = false)
public class SomeControllerTest {

Note by the author: As of 2021, this answer has been obsolete for a few years and it probably won't work for you.

Level answered 1/12, 2017 at 12:58 Comment(3)
secure was deprecated since 2.1.0 in favor of Spring Security's testing support. Now @AutoConfigureMockMvc(secure = false) shall be used.Karole
In @AutoConfigureMockMvc(secure = false), secure is deprecated tooFrancklyn
How to do for @SpringBootTest?Burgoyne
L
58

For me in Spring Boot 2.2.4 (JUnit5) the below seems to have worked and bypass the security filter.

@ExtendWith(SpringExtension.class)
@WebMvcTest(SomeController.class)
@AutoConfigureMockMvc(addFilters = false)
public class SomeControllerTest {
...

Note: this simply disables any filters in the SpringSecurity configuration. It won't disable the security completely. In other words it will still bootstrap security without loading any filters.

Lamonicalamont answered 25/2, 2020 at 15:40 Comment(1)
Note that @ExtendWith(SpringExtension.class) is not necessary, since it is imported automatically by @WebMvcTest.Zoller
A
25

In Spring Boot 2.2.6, @WebMvcTest is meta annotated with @AutoConfigureWebMvc which auto-configure org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration as you can see in spring.factories of spring-boot-test-autoconfigure.jar

So you just have to exclude SecurityAutoConfiguration in your test to disable Spring Security :

@WebMvcTest(excludeAutoConfiguration = SecurityAutoConfiguration.class) 
Anthropometry answered 27/4, 2020 at 13:11 Comment(2)
This worked for me when I had a hierarchy of classes in my test suite by annotating it to my base class. In case of the solution by using @AutoConfigureMockMvc , I had to do it for every class!Diuretic
Worked for me while using spring-security version 6.0.1, I just needed the controller tests to pass since I was just using the library for encryption and decryption and not any of the other security toolsDeina
L
21

You can set secure=false in the @WebMvcTest annoation. It will skip the spring security MockMvc auto configuration in your Test

@WebMvcTest(controllers = SomeController.class, secure = false)
public class SomeControllerTest {

Note by the author: As of 2021, this answer has been obsolete for a few years and it probably won't work for you.

Level answered 1/12, 2017 at 12:58 Comment(3)
secure was deprecated since 2.1.0 in favor of Spring Security's testing support. Now @AutoConfigureMockMvc(secure = false) shall be used.Karole
In @AutoConfigureMockMvc(secure = false), secure is deprecated tooFrancklyn
How to do for @SpringBootTest?Burgoyne
U
17

In Spring Boot 2.4 both secure flags were removed and none of the answers here actually work.

I ended up excluding all the security myself and wrapping it around in a custom annotation.

import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.AliasFor;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@WebMvcTest(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = WebSecurityConfigurer.class)},
            excludeAutoConfiguration = {SecurityAutoConfiguration.class,
                                        SecurityFilterAutoConfiguration.class,
                                        OAuth2ClientAutoConfiguration.class,
                                        OAuth2ResourceServerAutoConfiguration.class})
public @interface UnsecuredWebMvcTest {
    @AliasFor(annotation = WebMvcTest.class, attribute = "controllers")
    Class<?>[] value() default {};

    @AliasFor(annotation = WebMvcTest.class, attribute = "controllers")
    Class<?>[] controllers() default {};
}
Undertake answered 30/12, 2020 at 8:30 Comment(2)
When I implemented something similar, I didn't actually need the excludeFilters property, I'm presuming because that filter is only enabled by the configuration classes that are being excluded.Increment
Indeed, you should omit and/or append the security configuration you used in your project.Undertake
C
14

This worked for me, using spring boot 2.3.1.

@ExtendWith(SpringExtension.class)
@WebMvcTest(SomeController.class)
@AutoConfigureMockMvc(addFilters = false)
public class SomeControllerTest {
}
Cerated answered 19/7, 2020 at 5:58 Comment(2)
Note that @ExtendWith(SpringExtension.class) is not necessary, since it is imported automatically by @WebMvcTest.Zoller
The problem with this solution is that it disables more filters than just the security-related ones. That caused other problems for me, so I ended up using something similar to @skubski's answer.Increment
Z
13

With Spring Security 4+, I find @WithMockUser annotation to be very handy. It provides a mock user and password to test spring security methods annotated with @PreAuthorize or @PostAuthorize. All you need to do is annotate the test method with @WithMockUser. The default role for the user is USER. You can override the default username and role too.

//default
@Test
@WithMockUser
public void getProfile() {
   //your test here
} 

//with username and roles
@Test
@WithMockUser(username = "john", roles={"ADMIN"})
public void getProfile() {
   //your test here
} 

NOTE: This annotation can be used for classes.

@WithMockUser(username = "john", roles={"ADMIN"})
public class UsersAdminSecurityTest {
} 
Zamarripa answered 17/10, 2018 at 12:40 Comment(0)
H
4

I understand this is a specific question for Spring Boot 1.5 but seems a bit old. In order to have a successful OAuth2 secured controller unit test running I applied the following steps, kindly notice I used Spring Boot 2.2.6, Gradle 5.x, and JUnit 5. This mechanism should work the same way deprecated ones based on @AutoConfigureMockMvc(secure = false) or @WebMvcTest(controllers = SomeController.class, secure = false)

This is for a REST API project that is secured (OAuth2) using Microsoft's Azure Active Directory, but essentially this testing strategy should work for any OIDC, OAuth2 configuration.

The trick is to have a Controller test file and annotate it with @WebMvcTest annotation, however, the following parameters are required:

@WebMvcTest(
        value = YourController.class

        // this disables loading up the WebSecurityConfig.java file, otherwise it fails on start up
        , useDefaultFilters = false

        // this one indicates the specific filter to be used, in this case
        // related to the GreetController we want to test
        , includeFilters = {
        @ComponentScan.Filter(
                type = FilterType.ASSIGNABLE_TYPE,
                value = YourController.class
        )
        }
)

Here the configurations that make the test run successfully.

build.gradle

plugins {
    id 'org.springframework.boot' version '2.2.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.grailscoder'
version = '0.0.1-SNAPSHOT'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

ext {
    set('azureVersion', "2.2.4")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.microsoft.azure:azure-active-directory-spring-boot-starter'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.springframework.security:spring-security-test'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.5.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}

dependencyManagement {
    imports {
        mavenBom "com.microsoft.azure:azure-spring-boot-bom:${azureVersion}"
    }
}

test {
    useJUnitPlatform()
}

GreetController.java

package com.grailscoder.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
public class GreetController {

    @GetMapping("/greets")
    @PreAuthorize("hasRole('ROLE_USER')")   // This is validating against Active Directory's User role granted to the
                                            // current user.
    @ResponseStatus(HttpStatus.OK)
    public String getGreetMessage() {
        return "Greets from secret controller";
    }
}

WebSecurityConfig.java

package com.grailscoder.config;

import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter;
import lombok.RequiredArgsConstructor;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AADAppRoleStatelessAuthenticationFilter aadAuthFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);

        http.authorizeRequests()
                .antMatchers("/", "/index.html", "/public").permitAll()
                .anyRequest().authenticated();
        http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class);

    }
}

application.properties

azure.activedirectory.client-id=xxxxx-AD-client-id-goes-here
azure.activedirectory.session-stateless=true

GreetControllerTest.java

package com.grailscoder.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(
        value = GreetController.class

        // this disables loading up the WebSecurityConfig.java file, otherwise it fails on start up
        , useDefaultFilters = false

        // this one indicates the specific filter to be used, in this case
        // related to the GreetController we want to test
        , includeFilters = {
        @ComponentScan.Filter(
                type = FilterType.ASSIGNABLE_TYPE,
                value = GreetController.class
        )
        }
)
class GreetControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        // add setup stuff here
    }

    @Test
    @WithMockUser
    void testGreet() throws Exception {

        ResultActions result = mockMvc.perform(get("/greets"))
                .andExpect(status().isOk());
        System.out.println(result.andReturn().getResponse().getContentAsString());

    }


}

I understand in order to have a similar test JUnit 4 based with a completely different approach, the following test could be used as a reference (but I haven't tried): https://github.com/spring-projects/spring-security/blob/master/samples/boot/oauth2resourceserver/src/test/java/sample/OAuth2ResourceServerControllerTests.java

Haleakala answered 28/4, 2020 at 17:35 Comment(0)
L
4

In my case, for Spring Boot version 2.5.4, I'm able to bypass Jwt security by setting useDefaultFilters = false in @WebMvcTest

@WebMvcTest(controllers = YourController.class, useDefaultFilters = false)
public class YourControllerTest {
    // Test cases
}
Luminescent answered 12/10, 2021 at 9:23 Comment(2)
Definitely working to turn off WebSecurityConfig beans loading.Cioffi
When I tried this it disabled something that was mapping URLs to my controller endpoints. Every call using MockMvc was getting a 404.Increment
S
4
@AutoConfigureMockMvc(addFilters = false)

Just adding addFilters = false resolved this.

Superego answered 22/5, 2022 at 10:42 Comment(0)
P
4

For those looking for answers in 2023 - Spring Boot 3, Spring Security 6 - What worked for me excluding both OAuth2ResourceServerAutoConfiguration.class & SecurityAutoConfiguration.class

Option 1. Use 'excludeAutoConfiguration'

@WebMvcTest(controllers = MyController.class, 
    excludeAutoConfiguration = {
        OAuth2ResourceServerAutoConfiguration.class, 
        SecurityAutoConfiguration.class}
)

Option 2. Create your anotation

In my case I created this additional annotation which does effectively the same
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@ImportAutoConfiguration(exclude = {OAuth2ResourceServerAutoConfiguration.class, SecurityAutoConfiguration.class})
public @interface UnsecuredMvc {
})

Then you just use it like:

@UnsecuredMvc
@WebMvcTest(controllers = MyController.class)
public class MyTest {
Pandemic answered 29/12, 2023 at 16:31 Comment(2)
I had to exclude OAuth2ClientAutoConfiguration.class tooHoeg
In my case, only SecurityAutoConfiguration.class is needed.Hard
D
2

I solved the problem by using following annotations and properties:

@WebMvcTest(controllers =
    SomeController.class,
    excludeAutoConfiguration = {
        MySecurityConfig.class,
        ManagementWebSecurityAutoConfiguration.class,
        SecurityAutoConfiguration.class
    }
)
@ContextConfiguration(classes = SomeController.class)
public class SomeControllerTest {

}

NOTE: I' using spring boot 2.6.6, so secure=false didn't work for me!

Demavend answered 4/4, 2022 at 14:43 Comment(0)
O
0

@AutoConfigureMockMvc(secure = false) does not work because secure is deprecated

what works:

@AutoConfigureMockMvc(addFilters = false)

does not completely disable spring security just bypass its filter chain.


or

@WebMvcTest(excludeAutoConfiguration = {SecurityAutoConfiguration.class})

(and if you are using Spring Boot actuator:

@WebMvcTest(excludeAutoConfiguration = {SecurityAutoConfiguration.class,
        ManagementWebSecurityAutoConfiguration.class})

)

Ordonez answered 17/7, 2022 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.