JUnit单独运行正常但Maven构建时出现证书路径异常问题排查
我有一个JUnit测试用例,用来验证MockServer返回的JMS响应。单独运行这个测试用例时,能正常找到证书路径,没有异常;但通过Maven构建(比如
mvn test或mvn install)时,却抛出了sun.security.validator.ValidatorException,具体错误是PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target。
相关配置与代码
application.yml 证书配置
bridge: conduit: keystore: type: pkcs12 password: ENC(l4/yeZUp8n4v7iArP1H9OWeVHUO5JCM2) location: src/main/resources/config/certificatessl.p12 jasypt: encryptor: password: G;SobWzm9Z;zWOMQuUF
测试类中的MockServer代码
mockServer.when(HttpRequest.request("/gateway/spec").withSecure(true).withBody(Mockito.anyString())) .respond(HttpResponse.response(myJsonObject));
MockServer Bean配置
@Bean(destroyMethod = "stop") @Lazy public ClientAndServer mockServer() { ConfigurationProperties.deleteGeneratedKeyStoreOnExit(true); ClientAndServer mockServer = ClientAndServer.startClientAndServer(7071); HttpsURLConnection .setDefaultSSLSocketFactory(KeyStoreFactory.keyStoreFactory().sslContext().getSocketFactory()); return mockServer; }
application-cxfconduit.xml 配置
<http:conduit name="{/b2b/Services}PreparationPort.http-conduit"> <http:tlsClientParameters disableCNCheck="true" secureSocketProtocol="TLSv1.2"> <sec:keyManagers keyPassword="#{environment['bridge.conduit.keystore.password']}"> <sec:keyStore type="${bridge.conduit.keystore.type}" password="#{environment['bridge.conduit.keystore.password']}" file="${bridge.conduit.keystore.location}" /> </sec:keyManagers> </http:tlsClientParameters> <http:client Connection="close" ConnectionTimeout="${bridge.conduit.connectionTimeout:30000}" ReceiveTimeout="${bridge.conduit.receiveTimeout:120000}" ProxyServer="${bridge.conduit.proxy.address:}" ProxyServerPort="${bridge.conduit.proxy.port:}" ProxyServerType="HTTP" AutoRedirect="true" AllowChunking="false" /> <http:proxyAuthorization> <sec:UserName>"${bridge.conduit.proxy.username:}"</sec:UserName> <sec:Password>"${bridge.conduit.proxy.password:}"</sec:Password> </http:proxyAuthorization> </http:conduit>
这种单独运行正常但Maven构建出问题的情况,大多是环境、路径或加载时机的差异导致的,咱们一步步来排查:
1. 优先排查证书路径的相对路径问题
单独运行JUnit时,你的工作目录是项目根目录,但Maven构建时,测试阶段的工作目录默认是当前模块的target目录。你配置的证书路径是src/main/resources/config/certificatessl.p12,这是相对项目根的路径——在Maven构建时,这个路径指向的是target/src/main/resources/...,显然不存在。
解决方向:
- 改用类路径方式加载证书,把路径改成
classpath:config/certificatessl.p12,这样不管工作目录在哪,都能从编译后的target/classes目录下找到证书(Maven会自动把src/main/resources下的文件复制到target/classes)。 - 或者在Maven的
pom.xml中配置测试阶段的工作目录,让它和单独运行JUnit时一致:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <workingDirectory>${project.basedir}</workingDirectory> </configuration> </plugin>
2. 检查Maven资源复制是否遗漏证书
单独运行JUnit时,IDE会自动把src/main/resources加入类路径,但Maven构建时,只有被正确复制到target/classes的文件才能被加载。如果证书文件没被复制过去,自然会加载失败。
检查方法:
- 打开
target/classes/config/目录,确认certificatessl.p12是否存在。如果没有,检查pom.xml的<build><resources>配置,是否排除了.p12类型的文件,或者没有包含config目录:
<resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*</include> <include>**/*.p12</include> </includes> </resource> </resources>
3. 验证MockServer的SSL上下文初始化时机
你在MockServer的Bean中设置了HttpsURLConnection.setDefaultSSLSocketFactory,但Maven构建时,测试类的加载顺序可能和IDE单独运行时不同——比如@Lazy注解导致MockServer Bean延迟初始化,测试用例发起请求时还没加载自定义SSL上下文,仍使用默认的JVM信任库,从而触发PKIX异常。
解决方向:
- 去掉
@Lazy注解,确保MockServer Bean在测试用例执行前完全初始化。 - 在测试类上添加
@DependsOn("mockServer")注解,强制测试类依赖MockServer Bean的初始化。
4. 确认Jasypt解密的环境参数是否传递
单独运行JUnit时,IDE可能已经帮你设置了Jasypt的加密密码(比如通过环境变量或VM参数),但Maven构建时没有传递这个密码,会导致证书密码解密失败,进而无法加载密钥库,最终触发PKIX异常。
解决方向:
- 运行Maven时手动传递Jasypt密码参数(注意特殊字符需要转义,Windows下分号要加引号):
# Linux/Mac mvn test -Djasypt.encryptor.password='G;SobWzm9Z;zWOMQuUF' # Windows mvn test -Djasypt.encryptor.password="G;SobWzm9Z;zWOMQuUF"
- 或者在
pom.xml的maven-surefire-plugin中配置该参数:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>-Djasypt.encryptor.password=G;SobWzm9Z;zWOMQuUF</argLine> </configuration> </plugin>
5. 检查Maven测试阶段的JVM参数差异
IDE单独运行JUnit时,可能设置了自定义的JVM参数(比如指定了信任库路径),但Maven构建时用的是默认JVM参数,导致默认信任库不包含你的证书。
解决方向:
- 在
pom.xml的maven-surefire-plugin中直接配置SSL相关的JVM参数:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine> -Djavax.net.ssl.keyStore=${project.basedir}/src/main/resources/config/certificatessl.p12 -Djavax.net.ssl.keyStorePassword=解密后的明文密码 -Djavax.net.ssl.keyStoreType=PKCS12 </argLine> </configuration> </plugin>
6. 验证CXF Conduit的配置解析是否正确
Maven构建时,application-cxfconduit.xml中的占位符(比如${bridge.conduit.keystore.location})可能没有被正确解析,导致CXF无法找到证书路径。
检查方法:
- 开启Maven调试日志(
mvn test -X),搜索CXF初始化相关的日志,确认密钥库路径是否被正确替换为实际路径。 - 临时把
file属性改成绝对路径,看看是否能解决问题,以此验证路径解析是否是根源。
内容的提问来源于stack exchange,提问作者Myra

