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
- A Spring Boot project with security configuration for Keycloak described in the Keycloak with Spring Boot #1 – Configure Spring Security with Keycloak post.
- Dependencies that provide support for Spring Security and testing. Below are the relevant dependencies from my project:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!-- 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:
1 |
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:
1 2 |
# 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):
1 2 3 4 5 6 7 8 9 10 11 |
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 { … |
1 2 3 4 5 6 7 8 9 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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 tofalse
, - uses the
SecurityConfig::configureApiSecurity
method we defined earlier.
You can see my conifg class below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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
- Response to How to disable Keycloak question on StackOverflow
- Document how to use this if Keycloak is on the classpath