问题
I know that there are already similar questions, here and here, regarding this problem but every solution proposed failed to help me. There is also mention to this library in most of those answers but (with all due respect) I would like to avoid depending on an external library just to be able to test a simple controller.
So, I have a very simple api that is accessed using a bearer token generated by keycloak and I would like to test the controller. Something along these lines:
@Test
@DisplayName("Should be ok")
@WithMockUser
void whenCalled_shouldBeOk() throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
mockMvc.perform(
post("/api/url/something")
.content("{}")
.contentType(APPLICATION_JSON)
.with(authentication(authentication))
).andExpect(status().isOk());
}
The problem is that I will always get a null pointer exception thrown by the KeycloakDeploymentBuilder
for it's missing the adapter config. In our SecurityConfig we extend the KeycloakWebSecurityConfigurerAdapter
and do all the required configurations for the app to work but I am failing to mock/by-pass this process in the test. Normally I find my way around this authentication problems in the tests (when keycloak isn't used) with @WithMockUser annotation but not this time.
Isn't there way to mock the adapter or the filter process in order to by-pass this issue?
I have tried everything that was answered in the other questions (except the library) with no luck. If you have any clue that could be of help or at least point me in the correct direction (since this can be due to a lack of knowledge on spring security from my part) that would very much appreciated.
回答1:
As I already wrote in my answer to the first question you linked, @WithMockUser
populates the security context with a UsernamePaswordAuthenticationToken
when your code / conf probably expect a KeycloakAuthenticationToken
.
If you read carefully the same answer, you'll find an alternative to using my lib to do this: manually populate the security-context with a KeycloakAuthenticationToken
instance or mock in each test.
Minimal sample with Mockito I added to my repo:
@Test
public void test() {
final var principal = mock(Principal.class);
when(principal.getName()).thenReturn("user");
final var account = mock(OidcKeycloakAccount.class);
when(account.getRoles()).thenReturn(Set.of("offline_access", "uma_authorization"));
when(account.getPrincipal()).thenReturn(principal);
final var authentication = mock(KeycloakAuthenticationToken.class);
when(authentication.getAccount()).thenReturn(account);
// post(...).with(authentication(authentication))
// limits to testing secured @Controller with MockMvc
// I prefer to set security context directly instead:
SecurityContextHolder.getContext().setAuthentication(authentication);
//TODO: invoque mockmvc to test @Controller or test any other type of @Component as usual
}
Maybe, after you mesure how much this clutters your tests (there are very few claims set here) you'll reconsider using my lib (or copying from it as it's opensource).
With my annotation, above sample becomes:
@Test
@WithMockKeycloakAuth
public void test() throws Exception {
//TODO: invoque mockmvc to test @Controller or test any other type of @Component as usual
}
Regarding spring test config with Keycloak involved, you could dig a bit into the tests of spring-security-oauth2-test-webmvc-addons
module. You'd find a complete app using KeycloakAuthenticationToken with unit tests (and working test conf)
来源:https://stackoverflow.com/questions/62199105/how-to-unit-test-a-springboot-controller-secured-by-keycloak