How to unit test a SpringBoot controller secured by keycloak?

大兔子大兔子 提交于 2020-08-24 16:21:02

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!