【问题标题】:Disable Spring Security config class for @WebMvcTest in Spring Boot在 Spring Boot 中禁用 @WebMvcTest 的 Spring Security 配置类
【发布时间】:2018-05-15 13:37:27
【问题描述】:

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

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

因此,默认情况下,我的所有 URL 现在都受到身份验证和自行生成的密码的保护。

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

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

由于缺乏授权,现在到处都失败了。

问题:我可以告诉@Test 方法忽略授权,以便它们像以前一样继续成功吗?

如何防止 @EnableWebSecurity 配置类在特定的 @WebMvcTest 单元测试类中被选中?

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

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

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

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

 ....}

但它似乎不起作用。

注意:我使用的是 Spring Boot 1.5.8

【问题讨论】:

标签: java spring spring-mvc spring-boot spring-security


【解决方案1】:

对我来说,在 Spring Boot 2.2.4 (JUnit5) 中,以下似乎有效并绕过了安全过滤器。

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

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

【讨论】:

  • 注意@ExtendWith(SpringExtension.class)不是必须的,因为它是由@WebMvcTest自动导入的。
【解决方案2】:

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

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

作者注: 截至 2021 年,这个答案已经过时了几年,它可能对你不起作用。

【讨论】:

  • secure 已弃用 since 2.1.0 in favor of Spring Security's testing support。现在应该使用@AutoConfigureMockMvc(secure = false)
  • @AutoConfigureMockMvc(secure = false) 中,secure 也已弃用
  • @SpringBootTest怎么办?
【解决方案3】:

在 Spring Boot 2.2.6 中,@WebMvcTest 使用 @AutoConfigureWebMvc 进行元注释,它会自动配置 org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,正如您在 spring-boot-test 的 spring.factories 中看到的那样自动配置.jar

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

@WebMvcTest(excludeAutoConfiguration = SecurityAutoConfiguration.class) 

【讨论】:

    【解决方案4】:

    使用 Spring Security 4+,我发现 @WithMockUser 注释非常方便。它提供了一个模拟用户和密码来测试使用@PreAuthorize 或@PostAuthorize 注释的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 {
    } 
    

    【讨论】:

      【解决方案5】:

      这对我有用,使用 spring boot 2.3.1。

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

      【讨论】:

      • 注意@ExtendWith(SpringExtension.class)不是必须的,因为它是由@WebMvcTest自动导入的。
      【解决方案6】:

      在 Spring Boot 2.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 {};
      }
      

      【讨论】:

        【解决方案7】:

        我知道这是 Spring Boot 1.5 的一个特定问题,但似乎有点老了。为了成功运行 OAuth2 安全控制器单元测试,我应用了以下步骤,请注意我使用了 Spring Boot 2.2.6、Gradle 5.x 和 JUnit 5。这种机制的工作方式应该与基于 @987654322 的弃用机制相同@或@WebMvcTest(controllers = SomeController.class, secure = false)

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

        诀窍是要有一个Controller测试文件并使用@WebMvcTest注解对其进行注解,但是需要以下参数:

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

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

        我理解为了以完全不同的方法进行基于 JUnit 4 的类似测试,可以使用以下测试作为参考(但我没有尝试过):https://github.com/spring-projects/spring-security/blob/master/samples/boot/oauth2resourceserver/src/test/java/sample/OAuth2ResourceServerControllerTests.java

        【讨论】:

          【解决方案8】:

          就我而言,对于 Spring Boot 版本 2.5.4,我可以通过在 @WebMvcTest 中设置 useDefaultFilters = false 来绕过 Jwt 安全性

          @WebMvcTest(controllers = YourController.class, useDefaultFilters = false)
          public class YourControllerTest {
              // Test cases
          }
          

          【讨论】:

          • 肯定会关闭 WebSecurityConfig bean 加载。
          猜你喜欢
          • 1970-01-01
          • 2013-11-01
          • 2014-07-25
          • 2014-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-23
          • 2021-06-29
          相关资源
          最近更新 更多