I have a custom annotation with a custom filter to authorize incoming requests. The issue is that when I try to test my controllers with MockMvc
the Spring Security Context is reset after each use of MockMvc
therefore I can only run tests where I only call one endpoint.
Controller:
@RestController
class ElementController {
@RequestMapping(value = ["getElement/{id}"], produces = [APPLICATION_XML_VALUE], method = [GET])
@PreAuthorize("authentication.superUser")
fun getElement(
@PathVariable id: UUID
): String? {
return // call service
}
// For brevity I've removed the other endpoint but it's declaration is similar.
}
Test Class:
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles(INTEGRATION_TESTS)
@DirtiesContext
class ElementTest {
@Autowired
private lateinit var mockMvc: MockMvc
@Test
@WithMockCustomUser(superUser = true) // Custom Annotation created via Spring Docs
fun `Given When Then`() {
// Passes with correct authentication
mockMvc.perform(
put("putElement").content(/*data*/)
).andDo(print()).andExpect(status().isOk)
// Fails because authentication context is empty
val resultGet = mockMvc.perform(
get("getElement/someId")
).andDo(print()).andExpect(status().isOk)
}
}
The above call fails due to org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
I've tried looking through the code and it looks like MockMvc
is resetting the security context after every call it makes. I've looked through the docs and it looks like I've set everything up correctly.
I've also looked at this GitHub issue where it's states:
@WithUserDetails will establish the same user for ever request. This is as designed.
Should this mean that my custom annotation should work the same way?
I'd really appreciate the help.
Update:
As a work-around I am setting the Sprint Security Context manually before the second call but I'd like to find a permanent solution.
val authentication = // set authentication
SecurityContextHolder.getContext().authentication = authentication
val resultGet = mockMvc.perform(
get("getElement/someId")
).andDo(print()).andExpect(status().isOk)