Java 在Spring引导中禁用@WebMvcTest的Spring安全配置类

Java 在Spring引导中禁用@WebMvcTest的Spring安全配置类,java,spring,spring-mvc,spring-boot,spring-security,Java,Spring,Spring Mvc,Spring Boot,Spring Security,最近,我使用以下类将Spring Security添加到我的Spring Boot项目中: @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class MySecurityConfig { } 因此,默认情况下,我的所有URL现在都使用身份验证和自行生成的密码进行保护 问题在于我用于控制器单元测试的@WebMvcTest类中的所有测试: @RunWith(SpringRunner.clas

最近,我使用以下类将Spring Security添加到我的Spring Boot项目中:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig {
}
因此,默认情况下,我的所有URL现在都使用身份验证和自行生成的密码进行保护

问题在于我用于控制器单元测试的@WebMvcTest类中的所有测试:

@RunWith(SpringRunner.class)
@WebMvcTest(SomeController.class)
public class SomeControllerTest {...}
由于缺乏授权,现在到处都失败了

问题:我是否可以告诉@Test methods忽略授权,以便它们像以前一样继续成功

如何防止在特定的@WebMvcTest单元测试类上选取@EnableWebSecurity配置类

我希望已经到位的测试能够继续进行,并在以后单独测试身份验证功能

到目前为止,我已尝试在测试类中使用嵌套的配置类,以排除安全配置:

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

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

 ....}
但它似乎不起作用


注意:我正在使用Spring Boot 1.5.8

您可以在@WebMvcTest注释中设置secure=false。 它将跳过测试中的spring security MockMvc自动配置

@WebMvcTest(controllers = SomeController.class, secure = false)
public class SomeControllerTest {
作者的说明:
到2021年为止,这个答案已经过时几年了,可能对您不起作用。

对于Spring Security 4+,我发现
@WithMockUser
注释非常方便。它提供一个模拟用户和密码来测试带有@PreAuthorize或@posauthorize注释的spring安全方法。您所需要做的就是用
@WithMockUser
注释测试方法。用户的默认角色是
user
。您也可以覆盖默认用户名和角色

//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
} 
注意:此注释可用于类

@WithMockUser(username = "john", roles={"ADMIN"})
public class UsersAdminSecurityTest {
} 

对我来说,在SpringBoot2.2.4(JUnit5)中,下面的代码似乎起到了作用,绕过了安全过滤器

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

注意:这只是禁用SpringSecurity配置中的任何过滤器。它不会完全禁用安全性。换句话说,它仍然可以在不加载任何过滤器的情况下引导安全性。

在Spring Boot 2.2.6中,@WebMvcTest用@autoconfigurewebvc进行元注释,后者可以自动配置org.springframework.Boot.autoconfigure.security.servlet.SecurityAutoConfiguration,正如您在Spring-Boot-test-autoconfigure.jar的Spring.factories中看到的那样

因此,您只需在测试中排除SecurityAutoConfiguration即可禁用Spring安全性:

@WebMvcTest(excludeAutoConfiguration = SecurityAutoConfiguration.class) 

我知道这是Spring Boot 1.5的一个特定问题,但似乎有点过时了。为了成功地运行OAuth2安全控制器单元测试,我应用了以下步骤,请注意,我使用了SpringBoot2.2.6、Gradle5.x和JUnit5。此机制的工作方式应与基于
@AutoConfigureMockMvc(secure=false)
@WebMvcTest(controllers=SomeController.class,secure=false)

这适用于使用Microsoft Azure Active Directory进行安全保护(OAuth2)的REST API项目,但本质上,此测试策略适用于任何OIDC、OAuth2配置

诀窍是拥有一个控制器测试文件并使用@WebMvcTest annotation对其进行注释,但是,需要以下参数:

@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
        )
        }
)
下面是使测试成功运行的配置

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";
    }
}
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);

    }
}
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());

    }


}
WebSecurityConfig.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";
    }
}
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);

    }
}
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());

    }


}
应用程序属性

azure.activedirectory.client-id=xxxxx-AD-client-id-goes-here
azure.activedirectory.session-stateless=true
GreetControllerTest.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";
    }
}
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);

    }
}
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());

    }


}

我知道,为了使用完全不同的方法实现类似的JUnit4测试,可以使用以下测试作为参考(但我没有尝试):

这对我来说很有效,使用spring boot 2.3.1

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

在SpringBoot2.4中,两个
secure
标志都被删除,这里的答案实际上都不起作用

最后,我自己排除了所有安全性,并将其包装在自定义注释中

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 {};
}
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.oauth2clientutoconfiguration;
导入org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration;
导入org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
导入org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration;
导入org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
导入org.springframework.context.annotation.ComponentScan;
导入org.springframework.context.annotation.FilterType;
导入org.springframework.core.annotation.AliasFor;
导入org.springframework.security.config.annotation.web.websecurityconfigure;
导入java.lang.annotation.*;
@目标({ElementType.TYPE})
@保留(RetentionPolicy.RUNTIME)
@WebMvcTest(excludeFilters={@ComponentScan.Filter(type=FilterType.ASSIGNABLE_type,value=WebSecurityConfigurer.class)},
excludeAutoConfiguration={SecurityAutoConfiguration.class,
SecurityFilterAutoConfiguration.class,
OAuth2Client TautoConfiguration.class,
OAuth2ResourceServerAutoConfiguration.class})
public@interface UnsecuredWebMvcTest{
@别名(annotation=WebMvcTest.class,attribute=“controllers”)
类[]值()默认值{};
@别名(annotation=WebMvcTest.class,attribute=“controllers”)
类[]控制器()默认值{};
}

或者另一种方法是使用另一种安全配置进行测试,对其使用概要文件和ActiveProfiles注释
secure
自2.1.0以来被弃用
以支持Spring security的测试支持
。现在应该使用
@AutoConfigureMockMvc(secure=false)
。在
@AutoConfigureMockMvc(secure=false)
中,
安全
被弃用为
@springbootest
怎么办?即使这对我也不起作用。注意
@ExtendWith(Spri