Issue
I have a SpringBoot 2.3.1 application build with maven that loads some configuration from a application.yml
in src/main/resources
.
As a JAR the configuration is loaded from <jarfile>/BOOT-INF/classes/application.yml
.
As a WAR the configuration is not loaded (null), but the WAR contains it in <warfile>/WEB-INF/classes/application.yml
My relevant dependencies are:
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
...
and the profile is release.
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<project.packaging>jar</project.packaging>
</properties>
</profile>
<profile>
<id>release</id>
<properties>
<project.packaging>war</project.packaging>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
</profiles>
<build>
<defaultGoal>spring-boot:run</defaultGoal>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
I've tested that with
/** Main Entry Point for this Application */
@SpringBootApplication
public class Application extends SpringBootServletInitializer implements WebApplicationInitializer {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
log.info("Configure Servlet");
return builder.sources(Application.class);
}
}
And a config class like:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
@KeycloakConfiguration
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
...
@Value("${spring.application.name}")
private String name;
@Value("${keycloak.realm}")
private String realm;
/** Set Keycloak Config Resolver to use Spring Config */
@Bean
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
log.error("1-spring.application.name is: " + name);
log.error("1-keycloak.realm is: " + realm);
KeycloakSpringBootConfigResolver resolver = new KeycloakSpringBootConfigResolver();
return resolver;
}
@Bean(name = "MyTEST1")
String getString() {
log.error("2-spring.application.name is: " + name);
log.error("2-keycloak.realm is: " + realm);
return "Hello world";
}
In the log message, I can see during startup that the realm and name is null
in keycloakConfigResolver()
method but milliseconds later not null in getString()
. The 2-
logs are directly after the 1-..
logs in my log file.
2020-06-18 15:14:58.438 DEBUG 14536 --- [alina-utility-2] d.m.selfservices.platform.Application : Running with Spring Boot v2.3.1.RELEASE, Spring v5.2.7.RELEASE
2020-06-18 15:14:58.440 INFO 14536 --- [alina-utility-2] d.m.selfservices.platform.Application : No active profile set, falling back to default profiles: default
2020-06-18 15:14:59.711 INFO 14536 --- [alina-utility-2] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1233 ms
2020-06-18 15:15:00.192 ERROR 14536 --- [alina-utility-2] d.m.s.platform.SecurityConfig : 1-spring.application.name is: null
2020-06-18 15:15:00.192 ERROR 14536 --- [alina-utility-2] d.m.s.platform.SecurityConfig : 1-keycloak.realm is: null
2020-06-18 15:15:00.664 INFO 14536 --- [alina-utility-2] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@167c9b7e, org.springframework.security.web.context.SecurityContextPersistenceFilter@4c7dee5, org.springframework.security.web.header.HeaderWriterFilter@43d8836e, org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter@2d74b982, org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter@2d74b982, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter@1d951d12, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter@1d951d12, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@13256bad, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4f27b99b, org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter@2839e57e, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter@41fb5754, org.springframework.security.web.session.SessionManagementFilter@149db72c, org.springframework.security.web.access.ExceptionTranslationFilter@49d41456, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@2aa4e85a]
2020-06-18 15:15:00.683 ERROR 14536 --- [alina-utility-2] d.m.s.platform.SecurityConfig : 2-spring.application.name is: Plattform
2020-06-18 15:15:00.683 ERROR 14536 --- [alina-utility-2] d.m.s.platform.SecurityConfig : 2-keycloak.realm is: myrealm
2020-06-18 15:15:00.931 INFO 14536 --- [alina-utility-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-06-18 15:15:01.170 INFO 14536 --- [alina-utility-2] d.m.selfservices.platform.Application : Started Application in 3.277 seconds (JVM running for 1431.91)
I can confirm that behaviour on Tomcat 9.0.36, Wildfly 20 and with Spring Boot 2.2.6 and Keycloak 9.0.3 and 10.0.2. With embedded Tomcat everything is fine. When I intentionally misspell keycloak.realm
, I'm getting correctly an exception, that the value could not be resolved.
So, the properties are there, I can reach them with a simple bean definition, but during keycloakConfigResolver() it isn't available. I'm aware of the "new" - but that is the example from Keycloak.
What do I miss?
Solution
I presume the problem here is creation of the KeycloakSpringBootConfigResolver
via "new".
The resolver has an internal AdapterConfig
object. As part of some refactoring/bugfixing this object became non-static
some time ago. Before the change the static AdapterConfig
has been set centrally and all instances (constructed via new) could access that object. So this seems to be a bug within spring-keycloak.
The root cause here is, that KeycloakSpringBootConfigResolver
should have been created by Spring itself and the AdapterConfig
should have been declared an @Autowired
field (or been set via constructor).
Two possible solutions come to mind.
- Create your own
KeycloakSpringBootConfigResolver
(really not much code in there) using@Autowired
on theAdapterConfig
field (The injection will be done by Spring once yourkeycloakConfigResolver()
annotated method returns the instance - Set the
AdapterConfig
via reflection within yourkeycloakConfigResolver()
method
Answered By - Jonathan