Skip to content

Document how to use this if Keycloak is on the classpath #2

@wimdeblauwe

Description

@wimdeblauwe

Thank you for this project. It is just what I needed for my @WebMvcTest tests in my Spring Boot project. I am using Spring Boot 2.2.6 with keycloak-spring-boot-starter 9.0.0.

After adding:

        <dependency>
            <groupId>com.c4-soft.springaddons</groupId>
            <artifactId>spring-security-oauth2-test-webmvc-addons</artifactId>
            <version>2.0.0</version>
            <scope>test</scope>
        </dependency>

to my project, I also had to do the following things:

  1. Create a custom meta-annotation for all my webmvc tests:
@Retention(RetentionPolicy.RUNTIME)
@WebMvcTest
@ContextConfiguration(classes = MyProjectRestControllerTestConfiguration.class)
@ActiveProfiles("webmvc-test")
public @interface MyProjectRestControllerTest {
    /**
     * @see WebMvcTest#value
     */
    @AliasFor(annotation = WebMvcTest.class, attribute = "value")
    Class<?>[] value() default {};

    /**
     * @see WebMvcTest#controllers
     */
    @AliasFor(annotation = WebMvcTest.class, attribute = "controllers")
    Class<?>[] controllers() default {};

}
  1. I created a custom @TestConfiguration for my test:
@TestConfiguration
@Import({KeycloakAutoConfiguration.class, WebSecurityConfiguration.class})
public class MyProjectRestControllerTestConfiguration {
    @Bean
    public KeycloakSpringBootConfigResolver keycloakSpringBootConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

Where WebSecurityConfiguration is my actual production security config class:

@KeycloakConfiguration
public class WebSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder builder) {
        KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider();
        provider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        builder.authenticationProvider(provider);
    }

    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
            .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
            .antMatchers("/actuator/info", "/actuator/health").permitAll()
            .anyRequest()
            .hasRole("user");
    }
}
  1. Next, I added application-webmvc-test.properties in src/test/resources:
# Properties are here for proper startup. Keycloak is not actually used in @WebMvcTest tests
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.realm=pegus-digital-test
keycloak.resource=springboot-app
keycloak.public-client=true
keycloak.principal-attribute=preferred_username
  1. Finally my test self:
@MyProjectRestControllerTest(UserRestController.class)
class UserRestControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testOwnUserDetails() throws Exception {
        KeycloakAuthRequestPostProcessor keycloakAuthRequestPostProcessor = new KeycloakAuthRequestPostProcessor()
                .roles("user", "admin")
                .name("wim.deblauwe@example.com")
                .idToken(idToken -> {
                    idToken.setSubject("aac53545-5d5b-4e1f-8c94-0c593d22b5a2");
                    idToken.setGivenName("Wim");
                    idToken.setFamilyName("Deblauwe");
                });
        mockMvc.perform(get("/api/users/me")
                                .with(keycloakAuthRequestPostProcessor))
               .andExpect(status().isOk())
               .andExpect(jsonPath("authorizationServerUserId").value("aac53545-5d5b-4e1f-8c94-0c593d22b5a2"))
               .andExpect(jsonPath("firstName").value("Wim"))
               .andExpect(jsonPath("lastName").value("Deblauwe"))
               .andExpect(jsonPath("email").value("wim.deblauwe@example.com"))
               .andExpect(jsonPath("userRole").value("ADMIN"))
        ;
    }

}

Might be good to add something similar to the documentation, as most people will have Keycloak on the classpath in their project (unlike the example in your repo currently).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions