我正在开发一个提供REST HTTP(S)请求的Spring Boot应用程序。 (很常见)。
它按预期工作,但在最终(和工作)jar签名后(通过有效证书)所有URL映射都停止工作,只返回404到任何请求。 (请注意,嵌入式Tomcat服务器启动没有问题,我没有收到任何异常)
经过一些调试后,我发现Java的默认ClassLoader(Laucher $ AppClassLoader)在签名jar时不会返回我配置的包(@ComponentScan)中的类。
//org.springframework.core.io.support.PathMatchingResourcePatternResolver
//Param 'path' has my valid, existing and desired package with @Controller or @Component inside
protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
Set<Resource> result = new LinkedHashSet<Resource>(16);
ClassLoader cl = getClassLoader(); //sun.misc.Laucher$AppClassLoader
Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
//Empty enumeration when jar is signed
...
}
我尝试使用自定义类加载器但没有成功;同样的问题。
由于我在使用自签名证书对jar进行签名时起作用,我认为签名过程可能存在问题,这是由另一个人完成的。但我找不到任何证据。
看来,一旦签名,我就无法列出包裹内容......
如果我认为有用的话,我会尝试更多的测试并添加到这里......
更新
在自定义类加载器的帮助下调试后,我发现:
((java.net.JarURLConnection)new java.net.URL("jar:file:/home/user/my-app-with-dependencies_signed.jar!/META-INF/MANIFEST.MF").openConnection()).getJarEntry();
确定。的工作原理。
((java.net.JarURLConnection)new java.net.URL("jar:file:/home/user/my-app-with-dependencies_signed.jar!/META-INF/").openConnection()).getJarEntry();
不起作用! &GT;。&LT; 它抛出
Exception occurred in target VM: JAR entry META-INF/ not found in /home/user/my-app-with-dependencies_signed.jar
java.io.FileNotFoundException: JAR entry META-INF/ not found in /home/user/my-app-with-dependencies_signed.jar
at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:142)
at sun.net.www.protocol.jar.JarURLConnection.getJarEntry(JarURLConnection.java:94)
...
尝试访问未签名或自签名的jar时,第二个示例也适用。
当从@ComponentScan中的给定包中读取@Controller和@Component时,Spring执行打开jar的操作。
同样,Java的类加载器不会读取目录内容,只读取指定的文件。
this.getClass().getClassLoader(); //sun.misc.Launcher$AppClassLoader@18b4aac2
this.getClass().getClassLoader().getResources("META-INF/MANIFEST.MF").hasMoreElements(); //always true
this.getClass().getClassLoader().getResources("META-INF/").hasMoreElements(); //false when signed
更新2
我收到了有关签名的信息。负责签名和证书的人员实际上使用的Windows应用程序使用来自Windows-MY密钥库的证书和来自USB令牌的私钥对jar进行签名。
并非这当然是原因,但我认为重要的是要注意jarsigner
未被使用。
更新3
我创建了一个带有简单测试用例的github存储库: https://github.com/jesjobom/signed-jar-class-loader-test
答案 0 :(得分:1)
我已经达成了解决方案,但问题仍然存在。
加载我通过@ComponentScan
指向的类时,Spring会向ClassLoader(Laucher$AppClassLoader
)询问我通知的每个包的java.net.URL
。由于某些未知原因,我无法加载包/文件夹,因此我创建了一个自定义的ClassLoader,如果包是我的,它总是返回预期的URL。
public class CustomClassLoader extends ClassLoader {
...
@Override
public Enumeration<URL> getResources(String name) throws IOException {
if(name.startsWith("com/my/package/")) {
readBasePath(); //obtains path to jar (e.g. "jar:file:/home/app.jar!/")
List<URL> resources = new ArrayList<>();
resources.add(new URL(basePath + name));
return Collections.enumeration(resources);
}
return fallback.getResources(name); //default classloader
}
...
}
即便如此,稍后,Spring会尝试加载&#34; .class&#34;从包中失败也是出于同样的原因......所以,我创建了一个PathMatchingResourcePatternResolver
的自定义实现,它将列出jar的所有内容(我可以这样做!)并只选择给定包中的那些内容。
public class CustomPathMatchingResourceLoader extends PathMatchingResourcePatternResolver {
@Override
protected Set<Resource> doFindPathMatchingJarResources(final Resource rootDirResource, URL rootDirURL, String subPattern) throws IOException {
try {
String searchBase = ...; //package within jar
String pathBase = ...; //path to jar
URLConnection conn = new URL(pathBase).openConnection();
Set<Resource> resources = new HashSet();
JarFile file = ((JarURLConnection) conn).getJarFile();
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().startsWith(searchBase) && !entry.getName().endsWith("/")) {
resources.add(new UrlResource(pathBase + entry.getName()));
}
}
return resources;
} catch (Exception e) {
e.printStackTrace();
}
return super.doFindPathMatchingJarResources(rootDirResource, rootDirURL, subPattern);
}
...
}
所以它在签名过程中没有任何干扰......我非常有信心用jarsigner
签名可以解决问题,但我认为这很难......
无论如何,虽然它有效,但它不是解决方案。因此,我不会接受这个答案作为正确答案......
答案 1 :(得分:0)
问题是已签名的jar不会明确包含条目META-INF
和com/jesjobom
。
使用7zip,您可以列出zip条目:例如7za l signed-jar-class-loader-test_signed.jar
您的签名jar :
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2018-02-27 15:27:08 ..... 1907 999 com\jesjobom\Main.class
2018-02-27 15:27:08 ..... 2978 944 META-INF\maven\com.jesjobom\signed-jar-class-loader-test\pom.xml
2018-02-27 15:27:08 ..... 113 111 META-INF\maven\com.jesjobom\signed-jar-class-loader-test\pom.properties
2018-02-27 15:27:08 ..... 595 361 META-INF\MANIFEST.MF
2018-02-27 15:27:08 ..... 609 386 META-INF\BANCO_DO_BRASIL_SA.SF
2018-02-27 15:27:08 ..... 4520 3251 META-INF\BANCO_DO_BRASIL_SA.RSA
------------------- ----- ------------ ------------ ------------------------
2018-02-27 15:27:08 10722 6052 6 files
编译完后,签名版本中缺少一些 D
(目录)条目。
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2018-02-27 15:27:08 D.... 0 0 META-INF
2018-02-27 15:27:08 D.... 0 0 com
2018-02-27 15:27:08 D.... 0 0 com\jesjobom
2018-02-27 15:27:08 D.... 0 0 META-INF\maven
2018-02-27 15:27:08 D.... 0 0 META-INF\maven\com.jesjobom
2018-02-27 15:27:08 D.... 0 0 META-INF\maven\com.jesjobom\signed-jar-class-loader-test
2018-02-27 15:27:08 ..... 1907 999 com\jesjobom\Main.class
2018-03-09 14:15:16 ..... 3082 949 META-INF\maven\com.jesjobom\signed-jar-class-loader-test\pom.xml
2018-02-27 15:27:08 ..... 117 114 META-INF\maven\com.jesjobom\signed-jar-class-loader-test\pom.properties
------------------- ----- ------------ ------------ ------------------------
2018-02-27 15:27:08 5324 2221 4 files, 6 folders
我不知道文件夹条目丢失的原因。我怀疑因为他们在\META-INF\MANIFEST.MF
中没有SHA签名,所以他们会从签名的JAR中删除。