在构建 "maven-plugin" 包时如何使用 Proguard 混淆?

How do I use Proguard obfuscation while building a "maven-plugin" package?

"maven-plugin" 项目生成的输出 JAR(使用 maven-plugin-plugin) is broken by the obfuscation performed by the Proguard tool. Attempting to use the obfuscated JAR as a Maven plugin generates exceptions, such as a MojoExecutionException,它会因错误而终止构建。允许生成工作的正确 Proguard 配置选项是什么 "maven-plugin" JAR 包含自动生成的插件描述符?

Maven 插件和 Proguard 工具基础知识

为了生成Maven插件(maven封装类型"maven-plugin", which generates a JAR containing plugin specific configuration resources) we must instruct the maven-plugin-plugin on the location and name of our Mojos. Assuming a properly configured maven-plugin-plugin execution, either using annotations or other configuration options, the generated JAR will contain a plugin.xml file within the META-INF directory at the root of the JAR. This plugin.xml file describes your plugin's goals and configurable parameters using static references to Java class and package names (you can find more information on this file here)。

必须特别注意将混淆合并到“maven-plugin”JAR 的构建中;在这里我们解释了使用 Proguard obfuscation library. When using the default Proguard configuration for library obfuscation 所采取的步骤,生成的 JAR 将不再正常工作,因为 Proguard 重命名、收缩、重新定位和混淆 Maven 插件的重要文件。尝试使用您的插件可能会导致异常终止构建,并出现与 Maven 运行时无法定位和处理插件配置和 class 文件相关的错误。

然而,通过一些重新配置,我们可以指示 Proguard 正确维护生成的插件文件和“maven-plugin”JAR 的目录结构。 Proguard 选项的必要更改如下(请参阅有关以下 links 的注释):


自定义 Proguard 配置

目录结构

-keepdirectories

-keepdirectories

这指示 Proguard 维护输入 JAR 目录结构,而不是将所有文件移动到根目录。 Maven 期望 plugin.xml 文件位于 /META-INF/maven/ 目录中,该目录通过此选项与所有其他目录一起保留。您可以通过指定目录过滤器来更具体地过滤保留的目录,但是我选择不加区别地维护所有输入目录结构。

静态包引用

-keeppackagenames

-keeppackagenames org.apache.maven.plugin.my.MyMojo

您应该将占位符包替换为包含您的 Mojo 定义的包。如果您的 Mojo 定义不共享一个公共包,您应该根据需要使用多个选项指定每个唯一的包。如果您不确定需要保留哪些包,请在文本编辑器中打开生成的 plugin.xml 文件并检查每个“mojo”定义中的“实现”元素。 “implementation”元素通过完全限定名称指定 class。这些完全限定的 class 名称的每个包组件都是您应该指定的包。例如,我的 basedir-plugin 包含一个 Mojo 实现元素值“com.github.emabrey.maven.plugins.basedir.RootDirectoryGoal”,因此我会将选项写为 -keeppackagenames com.github.emabrey.maven.plugins.basedir.

静态Class引用

-keepnames

-keepnames class * implements org.apache.maven.plugin.AbstractMojo

此选项可防止 Proguard 重命名包含 Maven 插件 Mojo 实现的 classes。如果这些 classes 被重命名,上述“实现”元素将不再正确识别包含 Mojo 实现的 classes。

私有Class 字段和方法

-keepclassmembers

-keepclassmembers class * implements org.apache.maven.plugin.AbstractMojo {
    private <fields>;
    private <methods>;
}

此选项可防止 Proguard 在插件 Mojo 实现中重命名 class 级别的方法和字段。 Maven 使用这些 class fields/methods 的名称来生成插件的配置元素。如果 Proguard 重命名字段,Maven 执行环境将无法使用用户配置正确填充 Mojo 实现。


已完成 Proguard 配置

2.0.13 版的完整配置(more versions here; please see the notes regarding the plugin's version), including the default library configuration alongside the mentioned modifications, is provided here for your convenience (remember to specify the ${tool.proguard.version} property with the latest version of the proguard-base artifact 并将占位符包“org.apache.maven.plugin.my.MyMojo”替换为适当的值):

<!-- Configures Proguard obfuscation tool to generate an
     obfuscated version of the JAR file that replaces the
     default unobfuscated JAR.
-->
<plugin>
    <groupId>com.github.wvengen</groupId>
    <artifactId>proguard-maven-plugin</artifactId>
    <version>2.0.13</version>
    <executions>
        <execution>
            <id>obfuscation-packaging</id>
            <phase>package</phase>
            <goals>
                <goal>proguard</goal>
            </goals>
            <configuration>
                <proguardVersion>${tool.proguard.version}</proguardVersion>
                <obfuscate>true</obfuscate>
                <attach>true</attach>
                <appendClassifier>false</appendClassifier>
                <addMavenDescriptor>true</addMavenDescriptor>
                <injar>${project.build.finalName}.jar</injar>
                <injarNotExistsSkip>true</injarNotExistsSkip>
                <libs>
                    <lib>${java.home}/lib/rt.jar</lib>
                </libs>

                <options>
                    <option>-keepdirectories</option>
                    <option>-keeppackagenames org.apache.maven.plugin.my.MyMojo</option>
                    <option>-keepnames class * implements org.apache.maven.plugin.AbstractMojo</option>
                    <option>-keepclassmembers class * implements org.apache.maven.plugin.AbstractMojo {
                        private <![CDATA[<fields>]]>;
                        private <![CDATA[<methods>]]>;
                    }
                    </option>
                    <option>-keepparameternames</option>
                    <option>-renamesourcefileattribute SourceFile</option>
                    <option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                        SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
                    </option>
                    <option>-target 1.8</option>
                    <option>-keep public class * {
                        public protected *;
                        }
                    </option>
                    <option>-keepclassmembernames class * {
                        java.lang.Class class$(java.lang.String);
                        java.lang.Class class$(java.lang.String, boolean);
                        }
                    </option>
                    <option>-keepclasseswithmembernames,includedescriptorclasses class * {
                        native <![CDATA[<methods>]]>;
                        }
                    </option>
                    <option>-keepclassmembers,allowoptimization enum * {
                        public static **[] values();
                        public static ** valueOf(java.lang.String);
                        }
                    </option>
                    <option>-keepclassmembers class * implements java.io.Serializable {
                        static final long serialVersionUID;
                        private static final java.io.ObjectStreamField[] serialPersistentFields;
                        private void writeObject(java.io.ObjectOutputStream);
                        private void readObject(java.io.ObjectInputStream);
                        java.lang.Object writeReplace();
                        java.lang.Object readResolve();
                        }
                    </option>
                </options>
            </configuration>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>net.sf.proguard</groupId>
            <artifactId>proguard-base</artifactId>
            <version>${tool.proguard.version}</version>
        </dependency>
    </dependencies>
</plugin>  

注释

Link 问题 (9-04-2019)

Proguard 网站存在某种问题,这意味着我的 link 程序选项并不总是按照 anchor-containing link。如果您没有看到您点击的选项最初显示在他们的网页上,只需稍微向上滚动即可。

proguard-maven-plugin (9-04-2019)

的版本问题

截至 2017 年 3 月,com.github.wvengen:proguard-maven-plugin 的当前版本是 2.0.14,直到进行此编辑。我将完整保留版本号 2.0.13 的原始配置,因为版本 2.0.14 包含一个潜在的破坏性更改。它现在包括种子和映射文件,作为最终 Proguard 混淆工件的输出工件的一部分。大多数用例实际上不太可能在使用工件中的其他文件时出现任何问题,但我没有忍者编辑配置以指向 2.0.14,而是留下此注释并让您评估哪个版本适合你的项目。也就是说,只需将版本更改为 <version>2.0.14</version> 应该没问题,因为版本 2.0.14.

的提交历史记录中没有记录任何插件配置更改