You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Test:@WithMockUser不生效触发BadCredentialsException的解决

解决@WithMockUser模拟用户未生效的问题

问题场景

测试带有@PreAuthorize注解的接口方法时,使用@WithMockUser模拟用户未生效,系统抛出org.springframework.security.authentication.BadCredentialsException异常,会将127.0.0.1作为用户去数据库执行真实认证流程,需要修复以让模拟用户生效,跳过真实认证。

待测试接口方法

@PreAuthorize("hasAnyRole('ROLE_GST','ROLE_SUPER_GST','ROLE_CARGO_ASSURANCE_SUPER_USER', 'ROLE_CARGO_ASSURANCE_USER')")
@RequestMapping(value = "/task/bulkupload", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = {
        MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<ApInvoicesResponse<BulkOperation>> bulkUpload(@RequestPart(value = "file") MultipartFile documentToStore) {
    ApInvoicesResponse<BulkOperation> successfulResponse = new ApInvoicesResponse<>();
    try {
        BulkOperation bulkOperation = taskService.bulkUpload(documentToStore.getBytes());
        successfulResponse.setData(bulkOperation);
    } catch (Exception e) {
        logger.error("apinvoicesmessage:invalid document received : {} {}",e.getMessage(),e);
        
    }
    return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(successfulResponse);
}

原测试基类

@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public abstract class ControllerTest {

    
    @Autowired
    protected MockMvc mockMvc;

    @BeforeEach
    public void applySecurity() {
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        Authentication authentication =
                new TestingAuthenticationToken("username", "password", "ROLE_USER");
        context.setAuthentication(authentication);

        SecurityContextHolder.setContext(context);
    }
}

原测试方法

class ApInvoicesCmdControllerTest extends ControllerTest{

    @MockBean
    private TaskService taskService;

    @Test
    @WithMockUser(roles={"USER"})
    public void testBulkUpload_GSTUser_Successful() throws Exception {
        MockMultipartFile file = new MockMultipartFile("file", "test-file.txt", MediaType.TEXT_PLAIN_VALUE, "test data".getBytes());
        BulkOperation bulkOperation = new BulkOperation();
        Mockito.when(taskService.bulkUpload(file.getBytes())).thenReturn(bulkOperation);
        mockMvc.perform(MockMvcRequestBuilders.multipart("/task/bulkupload").file(file))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
                .andExpect(jsonPath("$.data").value(bulkOperation));
        verify(taskService, times(1)).bulkUpload(file.getBytes());
    }
}

问题原因

  1. 测试基类中@BeforeEach方法手动创建并设置了TestingAuthenticationToken到SecurityContext,会覆盖@WithMockUser注解自动注入的模拟认证信息,导致注解失效。
  2. 测试方法中指定的USER角色与接口要求的ROLE_GST等角色不匹配,即使注解生效也会触发权限校验失败。

修复方案

1. 删除基类手动设置认证的代码

移除ControllerTest中的@BeforeEach方法,让@WithMockUser自动管理SecurityContext,避免覆盖模拟用户信息。

修改后的测试基类:

@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public abstract class ControllerTest {

    @Autowired
    protected MockMvc mockMvc;
}

2. 调整测试方法的模拟角色

@WithMockUser的roles参数会自动添加ROLE_前缀,因此直接指定接口要求的角色名称即可匹配权限校验规则。

修改后的测试方法:

class ApInvoicesCmdControllerTest extends ControllerTest {

    @MockBean
    private TaskService taskService;

    @Test
    @WithMockUser(roles = {"GST"})
    public void testBulkUpload_GSTUser_Successful() throws Exception {
        MockMultipartFile file = new MockMultipartFile("file", "test-file.txt", MediaType.TEXT_PLAIN_VALUE, "test data".getBytes());
        BulkOperation bulkOperation = new BulkOperation();
        Mockito.when(taskService.bulkUpload(file.getBytes())).thenReturn(bulkOperation);
        
        mockMvc.perform(MockMvcRequestBuilders.multipart("/task/bulkupload").file(file))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
                .andExpect(jsonPath("$.data").exists());
        
        verify(taskService, times(1)).bulkUpload(file.getBytes());
    }
}

额外说明

  • @AutoConfigureMockMvc会自动集成Spring Security的测试支持,确保@WithMockUser等注解正确生效,无需手动管理SecurityContext。
  • 若需在多个测试中复用相同模拟用户,可直接在测试基类上添加@WithMockUser注解,无需在每个测试方法重复声明。

内容的提问来源于stack exchange,提问作者Abhishek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:47:03