Spring Boot

Keycloak with Spring Boot #2 – Spring Security instead of Keycloak in tests

Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can greatly simplify our codebase. However, it adds another external dependency that will complicate the integration testing. As a remedy, we can switch to native Spring Security when executing tests to verify only the business rules for access control instead of cluttering the code with Keycloak dependencies.

Prerequisites

<!-- pom.xml -->
…
<dependencies>
    …
    <!-- Security -->
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- END of Security -->

    <!-- Tests -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- END Tests -->
    …
</dependencies>
…
  • I’m working on a Spring Boot REST API that you can find in my keycloak-spring-boot repository. If you want to run the app locally, visit the project repository on GitHub and follow the directions in the README.md file.

Why we need to overwrite configuration in tests

For example, after we add Keycloak config to a project, Spring MVC tests will fail to load the ApplicationContext due to the following error:

java.io.FileNotFoundException: Unable to locate Keycloak configuration file: keycloak.json

Currently, there is no default way to disable Keycloak protection (note that the property keycloak.enabled=false is not supported).

While we could provide just enough Keycloak configuration to run the tests, it would introduce irrelevant details into the code. Therefore, we need to ensure a security config that keeps the test code clean and still allows API access rules to be verified.

Configure integration tests to use Spring Security instead of Keycloak

First, we’ll make the Keycloak setup dependent on a custom application property. Then we’ll configure our tests to use just Spring Security.

Make the Keycloak config load dependent on a custom property

We’re going to create a custom application property to control the loading of the security config. An important point to remember is that I want Keycloak to be used by default. In other words, I only want to turn it off in testing.

Create the application.properties file in the test/resources directory and copy the following property:

# src/test/resources/application.properties
security.config.use-keycloak=false

When executing tests, Spring Boot will only see the properties defined in this file. If you have any properties that should also be used in your tests, be sure to copy them to this file as well. The advantage of this approach is that it removes all irrelevant properties from the testing context and also provides a clear view of our testing setup.

Next, we need to configure our app to use Keycloak either when our custom property explicitly says so or when it hasn’t been specified at all (by default). Therefore, we’re going to aply the ConditionalOnProperty annotation to all Keycloak-related configuration classes (there are two in my project):

package in.keepgrowing.keycloakspringboot.security.config;
…
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

@KeycloakConfiguration
@ConditionalOnProperty(name = "security.config.use-keycloak", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
…
package in.keepgrowing.keycloakspringboot.security.config;
…
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(name = "security.config.use-keycloak", havingValue = "true", matchIfMissing = true)
public class KeycloakConfig {
…

Thanks to specifying the matchIfMissing attribute, the Keycloak configuration is our default security configuration. However, it won’t be used when running tests because we explicitly disabled it in the test/resources/application.properties file.

Define common security rules

As you may have noticed, we don’t have any security setup for our testing at the moment. Ideally, we want to test the business rules for API security without duplicating it in the tests. Therefore, I’m going to extract the common configuration into a static method so that I can reuse it in my tests:

package in.keepgrowing.keycloakspringboot.security.config;

import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

@KeycloakConfiguration
@ConditionalOnProperty(name = "security.config.use-keycloak", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    public static void configureApiSecurity(HttpSecurity http) throws Exception {
        http
                .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .and()
                .authorizeRequests()
                …
                .anyRequest().authenticated();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        configureApiSecurity(http);
    }
    …
}

As a result, I can reuse the SecurityConfig::configureApiSecurity method outside of the Keycloak configuration class.

Apply security configuration in tests

Consequently, we can now provide a separate security configuration for the tests. Create a class in the test package that:

  • uses the @Configuration annotation,
  • is enabled when the security.conifg.use-keycloak property is set to false,
  • uses the SecurityConfig::configureApiSecurity method we defined earlier.

You can see my conifg class below:

package in.keepgrowing.keycloakspringboot.security.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@ConditionalOnProperty(name = "security.config.use-keycloak", havingValue = "false")
public class TestSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        SecurityConfig.configureApiSecurity(http);
    }
}

This is all I need to apply my security config in tests without coupling it with an external authorization server.

Finally, by running tests from my ProductControllerTest class, I can verify the actual configuration in logs:

As we can see, a user password is generated automatically according to the default Spring Security specification and there is no mention of any Keycloak configuration.

More on using Spring Security instead of Keycloak in tests

Photo by ELEVATE from Pexels

little_pinecone

Recent Posts

Simplify the management of user roles in Spring Boot

Spring Security allows us to use role-based control to restrict access to API resources. However,…

3 years ago

Create a custom annotation to configure Spring Boot tests

A custom annotation in Spring Boot tests is an easy and flexible way to provide…

3 years ago

Keycloak with Spring Boot #4 – Simple guide for roles and authorities

Delegating user management to Keycloak allows us to better focus on meeting the business needs…

3 years ago

Keycloak with Spring Boot #3 – How to authorize requests in Swagger UI

Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…

3 years ago

Keycloak with Spring Boot #1 – Configure Spring Security with Keycloak

Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…

3 years ago

Kecloak in Docker #7 – How to authorize requests via Postman

Postman comes with a wide variety of OAuth 2.0 compliant configuration options that allow us…

3 years ago