You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUnit单独运行正常但Maven构建时出现证书路径异常问题排查

Maven构建时JUnit测试出现PKIX证书验证异常的排查思路

我有一个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:44:58