Spring boot не читает компоненты после подписания jar

Я разрабатываю приложение Spring Boot, которое обслуживает запросы REST HTTP (S). (довольно часто).

Он работает так, как предполагается, но после того, как окончательный (и рабочий) jar будет подписан (действующим сертификатом), все сопоставления URL-адресов перестают работать, возвращая только 404 на любой запрос. (Обратите внимание, что встроенный сервер Tomcat запускается без проблем, и я не получаю никаких исключений)

После некоторой отладки я обнаружил, что загрузчик классов Java по умолчанию (Laucher$AppClassLoader) просто не возвращает классы в пакетах, которые я настроил (@ComponentScan), когда jar подписан.

//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
            ...
}

Я безуспешно пытался использовать собственный загрузчик классов; та же проблема.

Поскольку это работает, когда я подписываю банку самоподписанным сертификатом, я думаю, что может быть проблема с процессом подписания, который был выполнен другим человеком. Но я не могу найти никаких доказательств этого.

Похоже, что после подписания я не могу перечислить содержимое пакета...

Я попробую еще несколько тестов и добавлю сюда, если сочту нужным...

ОБНОВЛЕНИЕ

После отладки с помощью пользовательского загрузчика классов я обнаружил, что:

((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();

Не работает! >.‹ Выбрасывает

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 выполняется Spring при чтении @Controller и @Component из заданных пакетов в @ComponentScan.

Точно так же загрузчик классов 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-токена.

Не то чтобы это было причиной, но я думаю, что важно отметить, что jarsigner не используется.

ОБНОВЛЕНИЕ 3

Я создал репозиторий github с простым тестовым примером: https://github.com/jesjobom/signed-jar-class-loader-test


person Jairton Junior    schedule 16.02.2018    source источник
comment
После прочтения официального документа кажется, что процесс подписания включает обновление MANIFEST.MF. docs.oracle.com/javase/tutorial/deployment/jar/intro. html Во втором случае кажется, что MANIFEST.MF отсутствует. Вы проверили содержимое вашей подписанной банки? Вы также можете попробовать spring-boot-maven-plugin, он может правильно переупаковать вашу банку. docs.spring. io/spring-boot/docs/current/reference/html/   -  person Syl    schedule 20.02.2018
comment
Как вы его запускаете? Это файл jar, и вы запускаете его с помощью java -jar или это сервлет? Как вы подписываете банку? Это плагин в maven или извне?   -  person Syl    schedule 20.02.2018
comment
Я проверил подпись с помощью jarsigner -verify. Так что все должно быть в порядке, включая MANIFEST.MF. Но я проверил некоторые записи в этом файле. Подпись сделана не мной, поэтому я не знаю процесса (но я посмотрю). Я знаю, что эта подпись действительна. И в своих тестах я запускаю банку, используя java -jar.   -  person Jairton Junior    schedule 20.02.2018
comment
Пожалуйста, не могли бы вы сравнить файлы в двух банках? Есть ли в одном JAR файл INDEX.LIST, а в другом нет? Можете ли вы загрузить два ваших JAR-файла или уменьшенный репро-кейс, чтобы мы могли его изучить?   -  person Rich    schedule 26.02.2018
comment
Я не смог найти INDEX.LIST или что-то подобное в обеих банках, подписанных или самоподписанных. Но я замечаю кое-что странное... С неподписанным или самоподписанным банком я могу перечислить каталоги, используя unzip -l my-app-self_signed.jar "*/". Но если сделать то же самое с подписанной версией, ничего не будет перечислено. Это не имеет смысла, так как папки существуют, внутри них есть файлы, но они не перечислены... Я имею в виду, что папка org/springframework/util/ существует, потому что в ней есть файл FileCopyUtils.class внутри, но только папка не будет указан в подписанном банке, пока он указан в самоподписанном.   -  person Jairton Junior    schedule 26.02.2018
comment
Я попытаюсь создать простое приложение для тестирования и загрузить все версии. Но это займет некоторое время из-за процесса подписи...   -  person Jairton Junior    schedule 26.02.2018
comment
Новое обновление с проектом для проверки проблемы. знак равно   -  person Jairton Junior    schedule 28.02.2018


Ответы (2)


Я нашел решение, но проблема все еще существует.

При загрузке классов, которые я указал через @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 пытается загрузить «.class» из пакетов и терпит неудачу по тем же причинам... Итак, я создал пользовательскую реализацию 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 решит проблему, но я думаю, что это будет сложно...

В любом случае, хотя это сработало, это не решение. Поэтому я не приму этот ответ как правильный...

person Jairton Junior    schedule 23.02.2018

Проблема в том, что подписанная банка не содержит записи META-INF и com/jesjobom явно.

Используя 7zip, вы можете просмотреть записи zip: например. 7za l signed-jar-class-loader-test_signed.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
    

Я не знаю, почему записи папки отсутствуют. Я подозреваю, что, поскольку у них нет подписи SHA в \META-INF\MANIFEST.MF, они удаляются из подписанного JAR.

person Andrei Damian-Fekete    schedule 09.03.2018