I am running a test using Spring's TestRestTemplate to execute 2 api calls in sequence. My Test has this infrastucture:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
Api call 1:
GETHttpSecurityallows this request always@PreAuthorize("isAnonymous()")- Automatically logs in the user using these:
:
final Authentication myCustomAuthentication = ...MyCustomObjectForNewLogin)
eventPublisher.publishEvent(new AuthenticationSuccessEvent(myCustomAuthentication));
SecurityContextHolder.getContext().setAuthentication(myCustomAuthentication);
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getSession(true).setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
Api call 2:
- POST
- @PreAuthorize("hasAuthority('MyAuthorityThatHeAlwaysHasWhenLoggedIn')")
The issue is:
On the second call I always get a 401 Unauthorized. I have traced this down to the following:
in the spring class AnonymousAuthenticationFilter#doFilter:96
This is happening:
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(
createAuthentication((HttpServletRequest) req));
if (logger.isDebugEnabled()) {
logger.debug("Populated SecurityContextHolder with anonymous token: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
for some reason it looks like the context lost the authentication and is hence being repopulated as an anonymous user. I read that this value is a ThreadLocal in some cases which is why i am also setting the attribute in the session so it can be re-loaded however it seems to not be reloaded. How can i either force it to be reloaded or inject the value into that holder. (Note this series of api calls works when done through postman. Only fails in the test).
I am executing the calls as follows:
@Autowired
private TestRestTemplate rest;
rest.getForEntity(UriComponentsBuilder.fromPath("/api1/").queryParam("myparm", "val").build(true).toUri(), String.class));
rest.postForEntity("/api2/", new HttpEntity<>(myExpectedObject, state), String.class)