执行请求后,MockMvc似乎很清晰SecurityContext(java.lang.IllegalArgumentException:身份验证对象不能为null)

sno*_*lli 4 java spring spring-data spring-data-mongodb mockmvc

我正在尝试使用SpringBoot + Spring Data Mongo + SpringMVC运行一些集成测试

我已经简化和通用化了代码,但是它应该能够通过以下测试重现该行为。

BookRepository界面中可以看到,我希望用户只能检索他拥有的书(@Query("{ 'ownerName' : '?#{principal?.username})),并且正在编写测试以执行POST来保存书,然后验证书的所有者是否设置正确。

出于此问题的目的,我已将测试简化为a GET,然后调用findAll()

问题

执行任何MockMvc请求后,会SecurityContext被清除,ThreadLocalSecurityContextHolderStrategy#clearContext()这会导致在我尝试调用时引发以下异常repository.findAll();

java.lang.IllegalArgumentException: Authentication object cannot be null

BookRepository.java

@RepositoryRestResource
public interface BookRepository extends MongoRepository<Book, String> {

    @Query("{ 'ownerName' : ?#{principal?.username} }")
    List<Book> findAll();  

}
Run Code Online (Sandbox Code Playgroud)

BookCustomRepositoryIntegrationTest.java

/**
 * Integrate data mongo + mvc
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BookCustomRepositoryIntegrationTest {

    @Autowired
    BookRepository repository;

    @Autowired
    MockMvc mockMvc;  

    @Test
    @WithMockUser
    public void reproduceBug() throws Exception {

        repository.findAll(); //Runs allright

        mockMvc.perform(get("/books")
                .contentType(APPLICATION_JSON_UTF8))
                .andExpect(status().isOk());

        repository.findAll(); //Throws exception: java.lang.IllegalArgumentException: Authentication object cannot be null


    }

}
Run Code Online (Sandbox Code Playgroud)

小智 5

您的情况不起作用,因为SecurityContextPersistenceFilter和FilterChainProxy过滤器清除了SecurityContextHolder,但是TestSecurityContextHolder(由填充WithSecurityContextTestExecutionListener)仍然包含SecurityContext。

试试这种方法:

@Test
@WithMockUser
public void reproduceBug() throws Exception {
    repository.findAll();
    mockMvc.perform(get("/books")
            .contentType(APPLICATION_JSON_UTF8))
            .andExpect(status().isOk());
    SecurityContextHolder.setContext(TestSecurityContextHolder.getContext());
    repository.findAll();
}
Run Code Online (Sandbox Code Playgroud)

  • 嗨@seregamorph,你的建议比你好。这是迄今为止我见过的最好的解决方法,所以我会赞成它。但是,我不相信在每次与 MockMvc 交互后手动设置安全上下文会导致非常易读的测试,所以我不会认为它是一个明确的解决方案。 (4认同)

Laz*_*ass 0

AutoConfigureMockMvc我认为您可以配置而不是使用注释MockMvc注释,如下所示:

\n\n
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class BookCustomRepositoryIntegrationTest {\n\n    @Before\n    public void setup() {\n        mockMvc = MockMvcBuilders\n                .webAppContextSetup(context)\n                .apply(springSecurity()) 1\n                .build();\n    }\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

文档所示所述:

\n\n
\n

为了将 Spring Security 与 Spring MVC Test 一起使用,有必要添加 Spring Security FilterChainProxy 作为过滤器。还需要添加 Spring Security\xe2\x80\x99s TestSecurityContextHolderPostProcessor 以支持在 Spring MVC Test with Annotations 中以用户身份运行。这可以使用 Spring Security\xe2\x80\x99s SecurityMockMvcConfigurers.springSecurity() 来完成。

\n
\n