我正在努力测试对受Spring Security保护的URL的访问控制。

配置如下所示:

    http
            .authorizeRequests()
            .antMatchers("/api/user/**", "/user").authenticated()
            .antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasAuthority("ADMIN")
            .anyRequest().permitAll();

测试类如下所示:
package com.kubukoz.myapp;

import com.kubukoz.myapp.config.WebSecurityConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import javax.transaction.Transactional;

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


@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {MyApplication.class, WebSecurityConfig.class})
@WebAppConfiguration
@TransactionConfiguration(defaultRollback = true)
@Transactional(rollbackOn = Exception.class)
public class MyApplicationTests {

    @Autowired
    private WebApplicationContext context;
    private MockMvc mockMvc;
    @Autowired
    private FilterChainProxy filterChainProxy;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .dispatchOptions(true)
                .addFilters(filterChainProxy)
                .build();
    }

    @Test
    public void testAnonymous() throws Exception {
        mockMvc.perform(get("/api/user/account")).andExpect(status().is3xxRedirection());
    }

    @Test
    public void testUserAccessForAccount() throws Exception{
        mockMvc.perform(get("/api/user/account")).andExpect(status().isOk());
    }
}

通过最后两个测试的最简单方法是什么?
@WithMockUser无法正常工作。

最佳答案

您不应该直接添加FilterChainProxy。相反,您应该按照the reference所示应用SecurityMockMvcConfigurers.springSecurity()。下面是一个示例:

mockMvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())
            .build();

结果是:
  • FilterChainProxy作为Filter添加到MockMvc中(如您所做)
  • 添加了TestSecurityContextHolderPostProcessor

  • 为什么需要TestSecurityContextHolderPostProcessor?原因是我们需要将当前用户从测试方法传达给所创建的MockHttpServletRequest。这是必需的,因为Spring Security的SecurityContextRepositoryFilter将覆盖SecurityContextHolder上的任何值,使其成为当前SecurityContextRepository(即SecurityContext中的HttpSession)找到的值。

    更新

    请记住,方法名称中包含角色的所有内容都会自动在传入的字符串前加上“ROLE_”前缀。

    根据您的评论,问题在于您需要更新配置以使用hasRole而不是hasAuthority(因为注释使用角色):
    .authorizeRequests()
                .antMatchers("/api/user/**", "/user").authenticated()
                .antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasRole("ADMIN")
                .anyRequest().permitAll();
    

    或者

    在Spring Security 4.0.2+中,您可以使用:
    @WithMockUser(authorities="ADMIN")
    

    10-05 22:38
    查看更多