使用MockMvc时为何出现403 Forbidden错误?已用@WithMockUser
解决Spring Security下Mock测试Delete接口返回403的问题
问题分析
从错误日志能看到Handler: Type = null,说明请求没有匹配到对应的Controller方法,这是403的核心原因,另外还有请求体缺失、CSRF保护、Mock对象不匹配等问题需要一并修复。
修复步骤
1. 修正请求路径匹配
你的Controller方法标注@DeleteMapping("/deleteitem"),如果Controller类没有添加@RequestMapping("/admin")注解,测试请求路径应该是/deleteitem,而非/admin/deleteitem/;如果Controller类有@RequestMapping("/admin"),则路径应为/admin/deleteitem(去掉末尾多余的斜杠)。
2. 确保@WithMockUser注解正确配置
确认测试方法或类上添加了@WithMockUser,且角色与接口权限要求一致:
@Test @WithMockUser(username = "tushar", roles = "ADMIN") void testDeleteItem() throws Exception { // 测试逻辑 }
3. 携带请求体(满足@RequestBody要求)
Controller方法需要接收DeleteItemDto作为请求体,测试时要通过content()传递JSON格式的请求体:
// 先注入ObjectMapper @Autowired private ObjectMapper objectMapper; // 在请求中添加请求体 MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.delete("/admin/deleteitem") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(new DeleteItemDto(401)))) // 传入对应itemId的Dto实例 .andReturn();
4. 处理CSRF保护
Spring Security默认开启CSRF,DELETE请求需要携带CSRF Token,测试中可以通过两种方式处理:
- 方式一:在请求中添加CSRF Token
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.delete("/admin/deleteitem") .with(csrf()) // 手动添加CSRF Token .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(new DeleteItemDto(401)))) .andReturn();
- 方式二:测试时关闭CSRF过滤(仅用于测试场景)
在测试类上添加注解:
@AutoConfigureMockMvc(addFilters = false)
5. 修正Mock对象匹配问题
Service中是删除findById返回的对象,测试时要保证Mock的对象一致:
// 创建并设置Mock的Item对象 ItemsModel mockItem = new ItemsModel(); mockItem.setItemId(401); // Mock findById返回该对象 Mockito.when(itemRepo.findById(401)).thenReturn(Optional.of(mockItem)); // Mock delete该对象的操作 doNothing().when(itemRepo).delete(mockItem);
完整修正后的测试代码示例
@Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @MockBean private ItemRepo itemRepo; @Test @WithMockUser(username = "tushar", roles = "ADMIN") void testDeleteItem() throws Exception { // 准备测试数据 ItemsModel mockItem = new ItemsModel(); mockItem.setItemId(401); DeleteItemDto deleteDto = new DeleteItemDto(401); // Mock Repository方法 Mockito.when(itemRepo.findById(401)).thenReturn(Optional.of(mockItem)); doNothing().when(itemRepo).delete(mockItem); // 发送请求并验证结果 MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.delete("/admin/deleteitem") .with(csrf()) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(deleteDto))) .andReturn(); assertEquals(200, mvcResult.getResponse().getStatus()); }
内容的提问来源于stack exchange,提问作者Tushar Kawade
相关产品推荐
相关产品推荐

