I am developing a web application using Spring Boot 3 with the Undertow web server. I want to integrate Keycloak as the authentication server for the application, which uses the returned token for authorization. After doing that, I encounter an error. However, everything works fine when I switch to using the Tomcat web server. Note that I am using Spring Boot 3.3.5 and Java 21.
Do I have any mistakes while configuring projects?
The error mentioned below:
.springframework.context.ApplicationContextException: Unable to start web server
at .springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:165) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:619) ~[spring-context-6.1.14.jar:6.1.14]
at .springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.5.jar:3.3.5]
at com.megaheart.mymoney.MymoneyApplication.main(MymoneyApplication.java:17) ~[classes/:na]
Caused by: java.lang.AbstractMethodError: Receiver class .keycloak.adapters.undertow.KeycloakServletExtension does not define or inherit an implementation of the resolved method 'abstract void handleDeployment(io.undertow.servlet.api.DeploymentInfo, jakarta.servlet.ServletContext)' of interface io.undertow.servlet.ServletExtension.
at io.undertow.servlet.core.DeploymentManagerImpl.handleExtensions(DeploymentManagerImpl.java:282) ~[undertow-servlet-2.3.18.Final.jar:2.3.18.Final]
at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:156) ~[undertow-servlet-2.3.18.Final.jar:2.3.18.Final]
at .springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory.createManager(UndertowServletWebServerFactory.java:331) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory.getWebServer(UndertowServletWebServerFactory.java:300) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:188) ~[spring-boot-3.3.5.jar:3.3.5]
at .springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162) ~[spring-boot-3.3.5.jar:3.3.5]
... 8 common frames omitted
Below is pom.xml, application.properties and configuration class:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=".0.0" xmlns:xsi=";
xsi:schemaLocation=".0.0 .0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.megaheart</groupId>
<artifactId>mymoney</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mymoney</name>
<description>My Money - Help you manage your money effectively</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<version>2.3.18.Final</version>
</dependency>
<dependency>
<groupId>.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>25.0.3</version>
</dependency>
<dependency>
<groupId>.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>25.0.3</version>
</dependency>
<dependency>
<groupId>.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties
server.address=0.0.0.0
server.port=6969
spring.application.name=hello
spring.main.allow-bean-definition-overriding=true
# Spring Boot Admin
spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.max-file-size=200MB
spring.servlet.multipart.max-request-size=215MB
# Keycloak configuration
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/hello_realm
OAuth2ResourceServerSecurityConfiguration.java
@Configuration
@EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration {
@Bean
SecurityFilterChain resourceServerSecurityFilterChain(
HttpSecurity http) throws Exception {
http.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
http.sessionManagement(sessions -> {
sessions.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}).csrf(csrf -> {
csrf.disable();
});
http.authorizeHttpRequests(requests -> {
requests.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll();
});
return http.build();
}
}
Thanks,