spring - @WithMockUser 不选择 Spring Security 身份验证凭据

标签 spring spring-mvc spring-boot spring-security spring-data

我已经使用 Spring Security 以经典方式在我的 Controller 中设置了基本身份验证,如下所示:

@EnableWebSecurity
@EnableGlobalMethodSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("user").roles("USER")
                .and()
                .withUser("admin").password("admin").roles("USER", "ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(....);
    }
}

说到测试点,我使用的是 @WithMockUser 注释我的测试。 GET 的测试可能如下所示:
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = SomeController.class)
public class SomeControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test1() {
                  mockMvc.perform(get(...)).andExpect(...);
    }

或者像这样的POST:
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = SomeController.class)
public class SomeControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test1() {
                  mockMvc.perform(post(...)).andExpect(...);
    }

然后发生了意想不到的事情:
  • 当测试方法没有用@WithMockUser 注释时,它会因为 401 状态(未授权)而失败,这是合理的,因为没有完成基本身份验证
  • 当一个测试方法只是用一个空的 @WithMockUser 注释时 不指定任何凭据 ,它开始通过,这是不合理的,因为我没有提供正确的身份验证数据(而是我将它们留空)
  • 此时,当填写一些正确的凭据时,测试方法也会通过,例如 @WithMockUser(username = "user", password = "user", roles = "USER")

  • 问题 : 这是怎么回事?如何纠正这种错误行为?

    看起来 Spring Security 已激活,但是我的测试未使用我希望使用的身份验证。我是否必须自己模拟身份验证数据?

    编辑 完整的配置方法如下
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(URL1).hasAnyRole(ROLE_ADMIN)
                .antMatchers(URL2).hasAnyRole(ROLE_USER)
                .antMatchers(URL3).permitAll()
                .and()
                .httpBasic()
                .and()
                .csrf().disable();
    }
    

    最佳答案

    这种行为背后有两个原因:

  • @WithMockUser注释不用于执行身份验证。它创建一个已经过身份验证的用户。默认情况下,他的凭据是 user :password
  • @WebMvcTest不执行 MySecurityConfig.java。 这个注解创建了 Spring mockMvc 对象 具有安全默认值 用于检测。这些安全默认值由 org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityAutoConfiguration 应用
    您可以通过在 MySecurityConfig 上放置断点来仔细检查这一点。方法并在 Debug模式下重新运行您的测试。断点未命中。

  • 解决问题 1

    只需将您的方法更改为 @WithMockUser 注释的作用。它给已经登录的用户。仍然可以通过指定具体的用户名、密码和角色来测试 url 安全性和角色配置。

    解决问题 2
    为所有集成测试创建一个基类。它将在应用 Spring Security 的情况下配置 mockMvc。另请注意 @SpringBootTest注解。现在测试 将使用 MySecurityConfig.java
    import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
    import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
    
    import org.junit.Before;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.security.web.FilterChainProxy;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.web.context.WebApplicationContext;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public abstract class IT {
        @Autowired
        protected WebApplicationContext wac;
    
        @Autowired
        private FilterChainProxy springSecurityFilterChain;
    
        protected MockMvc mockMvc;
    
        @Before
        public void applySecurity() {
            this.mockMvc = webAppContextSetup(wac)
                .apply(springSecurity(springSecurityFilterChain))
                .build();
        }
    }
    

    像这样重写测试。假设您使用 http 基本身份验证。证书在测试中提供。注意:没有模拟用户注释。
    package com.example.demo;
    
    import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    
    import org.junit.Test;
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    
    public class SomeControllerIT extends IT {
    
      @Test
      public void test1() throws Exception {
        mockMvc.perform(get("/some")
            .with(httpBasic("user", "user")))
            .andExpect(MockMvcResultMatchers.content().string("hello"));
      }
    }
    

    关于spring - @WithMockUser 不选择 Spring Security 身份验证凭据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47596283/

    相关文章:

    java - ThreadPoolExecutor future 任务-Spring bean注入(inject)异常-spring-beans-2.0

    javascript - 在 Ajax 调用中返回实体对象而不是字符串

    java - Spring Rest Controller @RequestBody 用于除类之外的接口(interface)

    java - 为什么 Spring MVC 以 404 响应并报告 "No mapping found for HTTP request with URI [...] in DispatcherServlet"?

    java - 未在 spring boot 中注册的自定义转换器

    spring - 使用@SpringBootTest 的集成测试不会选择使用 spring.factories 配置的库的自动配置

    java - 如何在 spring 中将属性设置为 WEB-INF 中的路径名?

    java - Spring 类路径资源被覆盖

    java - 定义 Freemarker EOL 格式(\n 而不是\r\n)

    java - 测试 Spring @MVC 注解