【问题标题】:Add user role to request in Spring MVC Test Framework在 Spring MVC 测试框架中添加用户角色以请求
【发布时间】:2014-01-19 23:40:43
【问题描述】:

今天开始在办公室学习 Spring Test MVC 框架,看起来很方便,但马上就遇到了一些严重的问题。花了几个小时谷歌搜索,但找不到与我的问题相关的任何内容。

这是我非常简单的测试类:

import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = WebAppContext.class)
public class ControllerTests {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = webAppContextSetup(wac).build();
    }

    @Test
    public void processFetchErrands() throws Exception {
        mockMvc.perform(post("/errands.do?fetchErrands=true"))
               .andExpect(status().isOk())
               .andExpect(model().attribute("errandsModel", allOf(
                   hasProperty("errandsFetched", is(true)),
                   hasProperty("showReminder", is(false)))));
    }
}

测试到达以下控制器,但由于未正确授权,第一个 if 子句失败。

@RequestMapping(method = RequestMethod.POST, params="fetchErrands")
public String processHaeAsioinnit(HttpSession session, HttpServletRequest request, ModelMap modelMap,
                                  @ModelAttribute(ATTR_NAME_MODEL) @Valid ErrandsModel model,
                                  BindingResult result, JopoContext ctx) {
  if (request.isUserInRole(Authority.ERRANDS.getCode())) {
    return Page.NO_AUTHORITY.getCode();
  }

  [...]
}

如何为MockMvcRequestBuilders.post() 创建的MockHttpServletRequest 添加用户角色,以便通过控制器的权限检查?

我知道MockHttpServletRequest 有一个方法addUserRole(String role),但由于MockMvcRequestBuilders.post() 返回一个MockHttpServletRequestBuilder,我从来没有接触过MockHttpServletRequest,因此无法调用该方法。

检查 Spring 源代码,MockHttpServletRequestBuilder 没有与用户角色相关的方法,也没有在该类中调用过 MockHttpServletRequest.addUserRole(String role),所以我不知道如何告诉它在请求中添加用户角色。

我能想到的只是将自定义过滤器添加到过滤器链并从那里调用自定义HttpServletRequestWrapper,以提供isUserInRole() 的实现,但对于这种情况来说这似乎有点极端。该框架肯定应该提供更实用的东西吗?

【问题讨论】:

    标签: java spring unit-testing spring-test-mvc


    【解决方案1】:

    我想我找到了easier way

    @Test
    @WithMockUser(username = "username", roles={"ADMIN"})
    public void testGetMarkupAgent() throws Exception {
    
        mockMvc.perform(get("/myurl"))
                .andExpect([...]);
    }
    

    您可能需要以下 maven 条目

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <version>4.0.4.RELEASE</version>
            <scope>test</scope>
        </dependency>
    

    【讨论】:

    • 根据您的安全实施,您可以使用 'authorities = {"ADMIN"}' 代替角色。
    【解决方案2】:

    Spring MVC Test 有 principal() 方法,允许模拟这种情况下的请求凭证。这是设置了一些模拟凭据的测试示例:

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration("classpath:spring/mvc-dispatcher-servlet.xml")
    public class MobileGatewayControllerTest {
    
    private MockMvc mockMvc;
    
    @Autowired
    private WebApplicationContext wac;  
    
    @Autowired
    private Principal principal;
    
    @Autowired
    private MockServletContext servletContext;
    
    @Before 
    public void init()  {
        mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }
    
    
    @Test
    public void testExampleRequest() throws Exception {
    
        servletContext.declareRoles("ROLE_1");
    
        mockMvc.perform(get("/testjunit")
        .accept(MediaType.APPLICATION_JSON)
        .principal(principal))
        .andDo(print())
        .andExpect(status().isOk())
        .andExpect(content().contentType("application/json"))
        .andExpect(jsonPath("$.[1]").value("test"));
    }
    
    }
    

    这是一个如何创建模拟主体的示例:

    @Configuration
    public class SetupTestConfig {
    
    @Bean
    public Principal createMockPrincipal()  {
        Principal principal = Mockito.mock(Principal.class);
        Mockito.when(principal.getName()).thenReturn("admin");  
        return principal;
    }
    

    }

    【讨论】:

    • 这不会将名称添加到角色中,我们的身份验证解决方案也不会将角色添加到主体名称中。测试生产中不会发生的事情的单元测试有什么意义?
    • 没错,我已经编辑了答案,让 request.isUserInRole() 在 Spring MVC 测试中工作的一种方法是注入 MockServletContext 本身而不是 MockHttpServletRequest 并声明一些角色如上所述。
    • 注射部位忘记了吗?只添加一个实例变量而不分配它不会解决这个问题。
    • 我已经在测试@Autowired private MockServletContext servletContext中对实例变量servletContext进行了自动装配,你的意思是调用servletContext.declareRoles()?
    • 啊,如果您为servletContext 自动装配定义配置,它会失败,但如果您省略配置,它会神奇地工作。多么合乎逻辑,当其他一切都反过来时。 :(
    【解决方案3】:

    您可以在请求中使用MockHttpServletRequest 之前注入RequestPostProcessor 来配置它。

    MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/")
                        .with(new RoleRequestPostProcessor("some role"));
    
    class RoleRequestPostProcessor implements RequestPostProcessor {
        private final String role;
    
        public RoleRequestPostProcessor(final String role) {
            this.role = role;
        }
    
        @Override
        public MockHttpServletRequest postProcessRequest(final MockHttpServletRequest request) {
            request.addUserRole(role);
            return request;
        }
    }
    

    【讨论】:

      【解决方案4】:

      还有一种简单的替代方法可以为单个请求设置特定用户角色。如果您只想作为授权用户执行单个操作(例如设置测试夹具),然后检查具有不同角色的用户是否可以执行某些操作,这可能会很方便:

      ResultActions registerNewUserAsAdmin(String username, String password) throws Exception {
          final SignUpRequest signUpPayload = new SignUpRequest(username, password);
      
          final MockHttpServletRequestBuilder registerUserRequest = post(SIGN_UP_URL)
              .with(user("admin").roles("ADMIN"))
              .contentType(MediaType.APPLICATION_JSON_UTF8)
              .content(jsonMapper.writeValueAsString(signUpPayload));
      
          return mockMvc.perform(registerUserRequest);
      }
      

      请参阅SecurityMockMvcRequestPostProcessors 了解更多详情。

      【讨论】:

        【解决方案5】:

        如果您使用的是权限而不是角色,请在请求中授予权限,如下所示。

        mockMvc.perform(
                    put(REQUEST_URL).param(PARM, VALUE)
                            .with(SecurityMockMvcRequestPostProcessors.user(USERNAME).authorities(new SimpleGrantedAuthority("ADMIN")))                        
                            .contentType(APPLICATION_FORM_URLENCODED)
            )
        

        【讨论】:

          猜你喜欢
          • 2015-01-02
          • 1970-01-01
          • 2018-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多