Mock SecurityContextHolder / Authentication always returns null

I know this question is asked a lot, but maybe I have some things that are especially important for this. I am trying to run some integration tests on a Spring Boot application that supports REST (not Spring MVC) and for some reason SecurityContextHolder.getContext().getAuthentication()

always returns null, even when used @WithMockUser

in a test. I'm not sure if this has to do with the use of profiles in the config classes, but so far we haven't had any problems with that.

Class

@Override
public ResponseEntity<EmployeeDTO> meGet() {
    Principal principal = SecurityContextHolder.getContext().getAuthentication();
    logger.debug("Endpoint called: me({})", principal);
    EmployeeDTO result;

    // Get user email from security context
    String email = principal.getName(); // NPE here

// ...
}

      

Test

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        properties = {"eureka.client.enabled:false"})
@WithMockUser
@ActiveProfiles(value = "test")
public class MeControllerTest extends IntegrationSpringBootTest {

@Autowired
private TestRestTemplate restTemplate;

@MockBean
private SecurityContext securityContext;

@MockBean
private Authentication authentication;

@MockBean
private EmployeeRepository employeeRepository;

@BeforeClass
public static void setUp() {

}

@Before
@Override
public void resetMocks() {
    reset(employeeRepository);
}

@Test
public void meGet() throws Exception {
    when(securityContext.getAuthentication()).thenReturn(authentication);
    securityContext.setAuthentication(authentication);
    when(authentication.getPrincipal()).thenReturn(mockEmployee());
    SecurityContextHolder.setContext(securityContext);
    when(employeeRepository.findByEmail(anyString())).thenReturn(mockEmployee());

    ResponseEntity<EmployeeDTO> employeeDTOResponseEntity =
            this.restTemplate.getForEntity("/me", EmployeeDTO.class);
// ...
}

      

If I return mock Principal

instead mockEmployee()

, the test cannot even start because this is happening:

org.springframework.beans.factory.BeanCreationException: Could not inject field: private org.springframework.security.core.Authentication com.gft.employee.controller.MeControllerTest.authentication; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'org.springframework.security.core.Authentication#0' is expected to be of type 'org.springframework.security.core.Authentication' but was actually of type '$java.security.Principal$$EnhancerByMockitoWithCGLIB$$657040e6'

      

Additional explanations. This Spring Boot application also uses OAuth2 for authorization, but it must be disabled for these tests. This is why we use profiles. Omitting the annotation @ActiveProfiles

gives us a 401 unauthorized error regarding the endpoint request.

I could use PowerMock, but I would like to avoid it if possible.

+3


source to share


1 answer


I ended up using MockMvc

, even though the application was not Spring MVC based. Also, I split the calls SecurityContext

to another service, but before that I could assert that the annotation was @WithMockUser

working correctly.

What's the key to doing this is to use these snippets at the class level:



@WebMvcTest(MeController.class)
@Import({ControllerConfiguration.class, BeanConfiguration.class})
public class MeControllerTest {
    // ...
}

      

Usage @WebMvcTest

makes it easier not to initialize SecurityContext

in the first place. You don't even need to call springSecurity()

. You can simply perform the operations mockMvc.perform()

as usual, and any calls to SecurityContext

return any mocked user that you specify, either with @WithMockUser

or by mocking the service that handles such a call.

+1


source







All Articles