jar签名后,Spring启动无法读取组件

时间:2018-02-16 20:26:32

标签: java spring spring-boot classloader jarsigner

我正在开发一个提供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

2 个答案:

答案 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-INFcom/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中删除。