使用jlink构建Docker镜像后Spring Boot应用JwtDecoder初始化异常
问题
Spring Boot应用在Eclipse中及直接运行Jar文件时均正常,但通过jlink构建Docker镜像后,应用能正常启动,但调用接口时抛出JwtDecoderInitializationException异常。
异常日志
2023-11-14 05:48:45.584 [http-nio-8080-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet].log(175) : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception org.springframework.security.oauth2.jwt.JwtDecoderInitializationException: Failed to lazily resolve the supplied JwtDecoder instance at org.springframework.security.oauth2.jwt.SupplierJwtDecoder.wrapException(SupplierJwtDecoder.java:53) at org.springframework.security.oauth2.jwt.SupplierJwtDecoder.lambda$new$0(SupplierJwtDecoder.java:39) at org.springframework.util.function.SingletonSupplier.get(SingletonSupplier.java:97) at org.springframework.security.oauth2.jwt.SupplierJwtDecoder.decode(SupplierJwtDecoder.java:49) at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt(JwtAuthenticationProvider.java:99) at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:88) at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) at org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:137) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) 2023-11-14 05:48:45.684 [http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet].log(175) : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception org.springframework.security.oauth2.jwt.JwtDecoderInitializationException: Failed to lazily resolve the supplied JwtDecoder instance at org.springframework.security.oauth2.jwt.SupplierJwtDecoder.wrapException(SupplierJwtDecoder.java:53)
构建镜像使用的Dockerfile
FROM amazoncorretto:21-alpine3.17-jdk as builder ARG ARTIFACTORY_USER ARG ARTIFACTORY_TOKEN WORKDIR /app COPY . /app RUN ./gradlew build -Partifactory.password=${ARTIFACTORY_TOKEN} -Partifactory.username=${ARTIFACTORY_USER} RUN ./gradlew copyDependencies RUN jdeps --ignore-missing-deps -q \ --multi-release 21 --print-module-deps \ --class-path="build/lib/*" \ --module-path="build/lib/*" build/libs/user-network-dev-04.10.2023.01-plain.jar > jre-deps.info RUN jlink --verbose \ --compress 2 \ --strip-java-debug-attributes \ --no-header-files \ --no-man-pages \ --output jre \ --add-modules $(cat jre-deps.info) FROM alpine:latest WORKDIR /application COPY --from=builder /app/jre /jre COPY --from=builder /app/build/lib/* lib/ COPY --from=builder /app/build/libs/user-network-dev-04.10.2023.01-plain.jar app.jar ENTRYPOINT ["/jre/bin/java", "-jar", "app.jar"]
原因分析与解决方案
核心原因
jdeps命令中使用的--ignore-missing-deps参数会忽略缺失的模块依赖,导致jlink构建的自定义JRE中缺少Spring Security OAuth2 JWT解码必需的关键模块(如证书解析、加密算法相关的java.naming、jdk.crypto.ec等),进而触发懒加载初始化失败。
解决步骤
移除jdeps的忽略参数
删除--ignore-missing-deps,让jdeps完整分析依赖,避免掩盖缺失问题:RUN jdeps --multi-release 21 --print-module-deps \ --class-path="build/lib/*" \ --module-path="build/lib/*" build/libs/user-network-dev-04.10.2023.01-plain.jar > jre-deps.info手动补充JWT必需模块
如果jdeps分析仍遗漏关键模块,可在jlink命令中直接添加:RUN jlink --verbose \ --compress 2 \ --strip-java-debug-attributes \ --no-header-files \ --no-man-pages \ --output jre \ --add-modules $(cat jre-deps.info),java.naming,jdk.crypto.ec,java.xml.crypto这些模块是JWT验证、证书解析过程的核心依赖。
验证自定义JRE完整性
镜像构建完成后,进入容器执行/jre/bin/java --list-modules,确认上述补充模块已包含。替代方案:使用Spring Boot官方分层镜像
若手动jlink配置繁琐,可直接用Spring Boot提供的镜像构建能力,自动处理依赖与JRE:FROM amazoncorretto:21-alpine3.17-jdk as builder WORKDIR /app COPY . . RUN ./gradlew bootBuildImage --image-name=your-image-name
内容的提问来源于stack exchange,提问作者Sreejesh
相关产品推荐
相关产品推荐

