Spring 来自终端的 Keycloak 运行 引导/安全类加载器问题
Spring Boot / Security classloader issues with Keycloak run from terminal
我将 Spring 启动和 Spring 安全与 Keycloak 结合使用。构建工具是 gradle.
当我 运行 ./gradlew bootRun
时,应用程序运行完美。如果我使用生成的 fat jar(即 java -jar myapp.jar
),应用程序将启动,但当应用程序尝试调用一些 keyloak 东西时我遇到异常:
java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenced from a method is not visible from class loader
at java.base/java.lang.reflect.Proxy$ProxyBuilder.ensureVisible(Proxy.java:851) ~[na:na]
at java.base/java.lang.reflect.Proxy$ProxyBuilder.validateProxyInterfaces(Proxy.java:682) ~[na:na]
at java.base/java.lang.reflect.Proxy$ProxyBuilder.<init>(Proxy.java:628) ~[na:na]
at java.base/java.lang.reflect.Proxy.lambda$getProxyConstructor(Proxy.java:426) ~[na:na]
at java.base/jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get(AbstractClassLoaderValue.java:327) ~[na:na]
at java.base/jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:203) ~[na:na]
at java.base/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:424) ~[na:na]
at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:999) ~[na:na]
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:79) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:131) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:93) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.keycloak.admin.client.Keycloak.realms(Keycloak.java:114) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
at org.keycloak.admin.client.Keycloak.realm(Keycloak.java:118) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
所以我发现我在终端中启动应用程序的方式一定有问题。我找到了 this official site,它解释了如何从终端 运行 一个 Spring 应用程序。所以我尝试了所有的解决方案,包括:
$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher
但我得到了同样的错误。
经过 2 天的搜索和试验,我没有任何想法。
所以我的问题基本上是:
有人知道如何解决这个问题吗?
我对类加载器的了解也有限 - 因此也非常欢迎这方面的任何实用提示。
编辑(添加 build.gradle):
Jackson 存在一些问题,这就是为什么直接包含 Spring 网络依赖项(没有启动器,因此没有 Jackson)。
这是 build.gradle:
buildscript {
ext {
kotlinVersion = '1.2.20'
springBootVersion = '1.5.7.RELEASE'
keycloakVersion = '3.4.3.Final'
restEasyClientVersion = '3.1.4.Final'
postgresServerVersion = '10.0'
postgresJdbcDriverVersion = '42.1.4'
spekVersion = '1.1.5'
jacksonVersion = '2.8.10'
javaxWsRsVersion = '2.1'
logbackVersion = '1.2.3'
slf4jVersion = '1.7.25'
}
repositories {
mavenCentral()
jcenter()
//spring dev
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
//Kotlin dev
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
//gradle plugins
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
classpath('com.bmuschko:gradle-docker-plugin:3.2.0')
//for tests
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
}
}
allprojects {
repositories {
mavenCentral()
jcenter()
//spring dev
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
}
}
subprojects {
repositories {
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
// for tests
maven { url "http://dl.bintray.com/jetbrains/spek" }
}
// for kotlin
apply plugin: 'kotlin'
// for tests
apply plugin: 'org.junit.platform.gradle.plugin'
apply plugin: 'kotlin-spring'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
// for tests
junitPlatform {
filters {
engines {
include 'spek'
}
}
}
sourceCompatibility = 1.9
dependencies {
// kotlin
compile("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
compile("org.jetbrains.kotlin:kotlin-test:${kotlinVersion}")
compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
// jackson
compile "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
compile "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:${jacksonVersion}"
compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:${jacksonVersion}"
compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.2"
// Java WS RS
compile "javax.ws.rs:javax.ws.rs-api:${javaxWsRsVersion}"
// for tests
testCompile "org.jetbrains.spek:spek-api:${spekVersion}"
testRuntime "org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}"
testCompile ("org.jetbrains.spek:spek-api:${spekVersion}") {
exclude group: 'org.jetbrains.kotlin'
}
testRuntime ("org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}") {
exclude group: 'org.junit.platform'
exclude group: 'org.jetbrains.kotlin'
}
// spring security
compile('org.springframework.boot:spring-boot-starter-security')
testCompile('org.springframework.security:spring-security-test')
// begin: spring web without jackson
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-tomcat')
compile('org.springframework:spring-web')
compile('org.springframework:spring-webmvc')
testCompile('org.springframework.boot:spring-boot-starter-test')
// end: spring web without jackson
// Keycloak
compile("org.keycloak:keycloak-spring-security-adapter:${keycloakVersion}")
compile("org.keycloak:keycloak-spring-boot-adapter:${keycloakVersion}")
compile("org.keycloak:keycloak-tomcat8-adapter:${keycloakVersion}")
compile("org.keycloak:keycloak-admin-client:${keycloakVersion}")
compile("org.jboss.resteasy:resteasy-client:${restEasyClientVersion}")
compile("org.jboss.resteasy:resteasy-jackson2-provider:${restEasyClientVersion}")
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
kotlinOptions.allWarningsAsErrors = true
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
}
问题出在 ForkJoinPool.commonPool
的底层 class 加载器,它被 CompletableFuture.supplyAsync
使用。
因为问题和解决方案很复杂为了更好地理解。
此问题仅针对此交叉引用而保留(并可能希望引导其他人找到正确的解决方案)。
我将 Spring 启动和 Spring 安全与 Keycloak 结合使用。构建工具是 gradle.
当我 运行 ./gradlew bootRun
时,应用程序运行完美。如果我使用生成的 fat jar(即 java -jar myapp.jar
),应用程序将启动,但当应用程序尝试调用一些 keyloak 东西时我遇到异常:
java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenced from a method is not visible from class loader
at java.base/java.lang.reflect.Proxy$ProxyBuilder.ensureVisible(Proxy.java:851) ~[na:na]
at java.base/java.lang.reflect.Proxy$ProxyBuilder.validateProxyInterfaces(Proxy.java:682) ~[na:na]
at java.base/java.lang.reflect.Proxy$ProxyBuilder.<init>(Proxy.java:628) ~[na:na]
at java.base/java.lang.reflect.Proxy.lambda$getProxyConstructor(Proxy.java:426) ~[na:na]
at java.base/jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get(AbstractClassLoaderValue.java:327) ~[na:na]
at java.base/jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:203) ~[na:na]
at java.base/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:424) ~[na:na]
at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:999) ~[na:na]
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:79) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:131) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:93) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.keycloak.admin.client.Keycloak.realms(Keycloak.java:114) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
at org.keycloak.admin.client.Keycloak.realm(Keycloak.java:118) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
所以我发现我在终端中启动应用程序的方式一定有问题。我找到了 this official site,它解释了如何从终端 运行 一个 Spring 应用程序。所以我尝试了所有的解决方案,包括:
$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher
但我得到了同样的错误。
经过 2 天的搜索和试验,我没有任何想法。
所以我的问题基本上是: 有人知道如何解决这个问题吗?
我对类加载器的了解也有限 - 因此也非常欢迎这方面的任何实用提示。
编辑(添加 build.gradle): Jackson 存在一些问题,这就是为什么直接包含 Spring 网络依赖项(没有启动器,因此没有 Jackson)。 这是 build.gradle:
buildscript {
ext {
kotlinVersion = '1.2.20'
springBootVersion = '1.5.7.RELEASE'
keycloakVersion = '3.4.3.Final'
restEasyClientVersion = '3.1.4.Final'
postgresServerVersion = '10.0'
postgresJdbcDriverVersion = '42.1.4'
spekVersion = '1.1.5'
jacksonVersion = '2.8.10'
javaxWsRsVersion = '2.1'
logbackVersion = '1.2.3'
slf4jVersion = '1.7.25'
}
repositories {
mavenCentral()
jcenter()
//spring dev
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
//Kotlin dev
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
//gradle plugins
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
classpath('com.bmuschko:gradle-docker-plugin:3.2.0')
//for tests
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
}
}
allprojects {
repositories {
mavenCentral()
jcenter()
//spring dev
maven { url 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
}
}
subprojects {
repositories {
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
// for tests
maven { url "http://dl.bintray.com/jetbrains/spek" }
}
// for kotlin
apply plugin: 'kotlin'
// for tests
apply plugin: 'org.junit.platform.gradle.plugin'
apply plugin: 'kotlin-spring'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
// for tests
junitPlatform {
filters {
engines {
include 'spek'
}
}
}
sourceCompatibility = 1.9
dependencies {
// kotlin
compile("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
compile("org.jetbrains.kotlin:kotlin-test:${kotlinVersion}")
compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
// jackson
compile "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
compile "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:${jacksonVersion}"
compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:${jacksonVersion}"
compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.2"
// Java WS RS
compile "javax.ws.rs:javax.ws.rs-api:${javaxWsRsVersion}"
// for tests
testCompile "org.jetbrains.spek:spek-api:${spekVersion}"
testRuntime "org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}"
testCompile ("org.jetbrains.spek:spek-api:${spekVersion}") {
exclude group: 'org.jetbrains.kotlin'
}
testRuntime ("org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}") {
exclude group: 'org.junit.platform'
exclude group: 'org.jetbrains.kotlin'
}
// spring security
compile('org.springframework.boot:spring-boot-starter-security')
testCompile('org.springframework.security:spring-security-test')
// begin: spring web without jackson
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-tomcat')
compile('org.springframework:spring-web')
compile('org.springframework:spring-webmvc')
testCompile('org.springframework.boot:spring-boot-starter-test')
// end: spring web without jackson
// Keycloak
compile("org.keycloak:keycloak-spring-security-adapter:${keycloakVersion}")
compile("org.keycloak:keycloak-spring-boot-adapter:${keycloakVersion}")
compile("org.keycloak:keycloak-tomcat8-adapter:${keycloakVersion}")
compile("org.keycloak:keycloak-admin-client:${keycloakVersion}")
compile("org.jboss.resteasy:resteasy-client:${restEasyClientVersion}")
compile("org.jboss.resteasy:resteasy-jackson2-provider:${restEasyClientVersion}")
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
kotlinOptions.allWarningsAsErrors = true
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
}
问题出在 ForkJoinPool.commonPool
的底层 class 加载器,它被 CompletableFuture.supplyAsync
使用。
因为问题和解决方案很复杂
此问题仅针对此交叉引用而保留(并可能希望引导其他人找到正确的解决方案)。