I recently upgraded one of my services to Spring Boot 2.1.5.RELEASE FROM 2.0.9.RELEASE. And I can't really go back, because I need some of the new features. The Spring Security Version pulled by Spring Boot is 5.1.5.RELEASE. Furthermore I'm using spring-security-jwt
, version 1.0.10.RELEASE, and spring-security-oauth2', version 2.3.6.RELEASE.'
All unit and integration tests are fine and I was just about to release it when the "real world" test that uses the jar-file built by gradle blew up. When trying to obtain a token from '/oauth/token' I get the exception shown below.
The frustrating thing now is that the endpoint will work perfectly if I run it using gradle bootrun
. But If I build the jar with gradle bootjar
and then run it with java -jar service.jar
the exception will show up.
Maybe someone already had this experience and knows what to do. I'm not sure which part of my code I could post to help investigating the issue and I'm not allowed to make the project public on GitHub.
ERROR org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.handleException:169
Handling error: IllegalStateException, This object has not been built
java.lang.IllegalStateException: This object has not been built
at org.springframework.security.config.annotation.AbstractSecurityBuilder.getObject(AbstractSecurityBuilder.java:55) ~[spring-security-config-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:508) ~[spring-security-config-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
at org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter.getOAuth2Authentication(ResourceOwnerPasswordTokenGranter.java:71) ~[spring-security-oauth2-2.3.6.RELEASE.jar!/:?]
at org.springframework.security.oauth2.provider.token.AbstractTokenGranter.getAccessToken(AbstractTokenGranter.java:72) ~[spring-security-oauth2-2.3.6.RELEASE.jar!/:?]
at org.springframework.security.oauth2.provider.token.AbstractTokenGranter.grant(AbstractTokenGranter.java:67) ~[spring-security-oauth2-2.3.6.RELEASE.jar!/:?]
at org.springframework.security.oauth2.provider.CompositeTokenGranter.grant(CompositeTokenGranter.java:38) ~[spring-security-oauth2-2.3.6.RELEASE.jar!/:?]
at org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer$4.grant(AuthorizationServerEndpointsConfigurer.java:583) ~[spring-security-oauth2-2.3.6.RELEASE.jar!/:?]
at org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:132) ~[spring-security-oauth2-2.3.6.RELEASE.jar!/:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_211]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_211]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_211]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_211]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) [spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) [spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) [spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) [spring-webmvc-5.1.7.RELEASE.jar!/:5.1.7.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:665) [javax.servlet-api-4.0.1.jar!/:4.0.1]
...
...
...
UPDATE:
I digged a little deeper. The "problematic" code resides in WebSecurityConfigurerAdapter
. In the bootRun-case delegateBuilder
in line 508 is a org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$DefaultPasswordEncoderAuthenticationManagerBuilder
which has already been built and therefore getObject
succeeds and delegate is not null anymore for future calls. In the bootJar-case the delegateBuilder
is of the same class, but the internal building
-flag is false
. And that's why the exception is thrown.
500: public Authentication authenticate(Authentication authentication)
501: throws AuthenticationException {
502: if (delegate != null) {
503: return delegate.authenticate(authentication);
504: }
505:
506: synchronized (delegateMonitor) {
507: if (delegate == null) {
508: delegate = this.delegateBuilder.getObject();
509: this.delegateBuilder = null;
510: }
511: }
512:
513: return delegate.authenticate(authentication);
514: }
UPDATE: The build file:
buildscript {
ext {
springBootVersion = '2.1.5.RELEASE'
}
repositories {
jcenter()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
}
}
apply plugin: "java"
apply plugin: "eclipse"
apply plugin: "org.springframework.boot"
apply plugin: "io.spring.dependency-management"
//Java version
sourceCompatibility = 1.8
targetCompatibility = 1.8
defaultTasks "bootRun"
/* Configuration of the application package */
jar {
baseName = "service"
version = "1.0.0"
}
bootJar {
manifest {
attributes("Manifest-Version": "1.0",
"Specification-Title": "Example Service",
"Specification-Version": "1.0",
"Specification-Vendor": "Company",
"Implementation-Title": "com.company.service",
"Implementation-Version": "1.0.0",
"Implementation-Vendor": "Company Inc.")
}
}
repositories {
jcenter()
mavenCentral()
maven { url "https://repo.spring.io/libs-release" }
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
}
dependencies {
implementation group:"org.springframework.boot", name:"spring-boot-starter-jersey"
implementation group:"org.springframework.boot", name:"spring-boot-starter-tomcat"
implementation group:"org.springframework.boot", name:"spring-boot-starter-log4j2"
implementation group:"org.springframework.boot", name:"spring-boot-starter-actuator"
implementation group:"org.springframework.boot", name:"spring-boot-devtools"
implementation group:"org.apache.logging.log4j", name:"log4j-jul"
implementation group:"javax.servlet", name:"javax.servlet-api"
implementation group:"org.glassfish.jersey.containers", name:"jersey-container-servlet"
implementation group:"org.glassfish.jersey.inject", name:"jersey-hk2"
implementation group:"org.glassfish.jersey.ext", name:"jersey-spring4"
implementation group:"org.springframework", name:"spring-context"
implementation group:"org.springframework", name:"spring-web"
implementation group:"org.springframework", name:"spring-aspects"
implementation group:"org.springframework.security", name:"spring-security-core"
implementation group:"org.springframework.security", name:"spring-security-web"
implementation group:"org.springframework.security", name:"spring-security-config"
implementation group:"org.springframework.security", name:"spring-security-jwt", version:"1.0.10.RELEASE"
implementation group:"org.springframework.security.oauth", name:"spring-security-oauth2", version:"2.3.6.RELEASE"
implementation group:"org.jasypt", name:"jasypt", version:"1.9.3"
implementation group:"org.thymeleaf", name:"thymeleaf-spring5"
implementation group:"org.springframework.data", name:"spring-data-jpa"
implementation group:"org.hibernate", name:"hibernate-core"
implementation group:"org.apache.tomcat", name:"tomcat-jdbc", version:"9.0.1"
implementation group: "org.postgresql", name:"postgresql", version:"42.2.5"
implementation group:"org.apache.commons", name:"commons-text", version:"1.6"
implementation group:"commons-validator", name:"commons-validator", version:"1.6"
testImplementation group:"org.springframework.boot", name:"spring-boot-starter-test"
testImplementation group:"org.apache.httpcomponents", name:"httpclient"
}
configurations.all {
exclude group:"org.springframework.boot", module: "spring-boot-starter-logging"
exclude group:"ch.qos.logback", module:"logback-core"
exclude group:"commons-logging", module:"commons-logging"
}
UPDATE:
Here is a repository that let's you reproduce the problem.
The problem will go away if I remove EncryptionConverter
from the project or just remove the @Autowired
Encryptor. Of course this is not a solution since actually autowiring something into a Converter
was one of the main reasons for upgrading Spring Boot in the first place. Very interesting is also that the problem also will go away if I remove the class PasswordResetManagerImpl
which is easy to de because it isn't used in the example project anyway. Exactly speaking it suffices to remove the injected PersistenceContext
in that class. I guess that those to classes are in conflict somehow.
But again, this is only a problem when starting the jar-file. Just running the project with bootRun?
still works perfectly fine.