包含内容
- JAVA混淆
- JS、CSS文件压缩
- 项目版本号及相关版本信息自动设置
- 不同环境下配置文件修改
开发环境
项目:基于Spring、SpringMVC和JPA框架搭建。
Proguard:4.9,开源混乱器,支持脚本控制,可以使用GUI界面,重命名a-z A-Z等单个字符名称。字符串不加密,支持 J2ME。
Proguard插件:com.github.wvengen.proguard-maven-plugin.2.0.6
YUICompressor:基于Rhino 对JavaScript源文件进行分析和切词,可以是去掉JavaScript文件和Css文件中冗余的空白字符(空格,换行符,制表符),对于JavaScript文件还可以对其进行混淆,更改局部变量的名称;对于CSS,还有采用优化0值属性值的表示,优化颜色值的方法压缩文件;能够融合多个js或css文件为一个文件。
yuicompressor-maven-plugin:在maven中调用yuicompressor,参考页面:https://github.com/davidB/yuicompressor-maven-plugin。
maven-antrun-plugin:能够在maven中运行ant任务,例如:文件目录操作、文件内容替换等。参考页面:http://maven.apache.org/plugins/maven-antrun-plugin/index.html
参考信息
Maven core :http://maven.apache.org/ref/3.2.3/maven-core/,介绍maven生命周期等核心概念。
Maven中profile基本使用方法:http://lpyyn.iteye.com/blog/2110927
Maven Assembly:maven打包插件,http://maven.apache.org/plugins/maven-assembly-plugin/
Proguard插件资源库地址:http://repository.pentaho.org/artifactory/repo/
ant中任务命令参考:http://ant.apache.org/manual/Tasks/,ant命令分为task和type,task是直接可以运行的命令,而type是在task中属性信息。
混淆过程
在打包前混淆class类及相关依赖类,在打包时使用assembly插件重新打带混淆的war包。
具体实现过程如下:
1. 定义profile,在打包过程中可以根据参数选择正常打包或混淆打包。
2. 配置proguard插件。在package执行proguard目标。
3. 配置assembly插件。在package执行single命令。
4. 调用方式。在根项目路径下执行mvn clean package -Pobfuscate,在子项目的pom中如果存在id=obfuscate的profile,则执行profile中对应的内容,否则不执行。
5. 混淆后的类及内容示例如下。
pom.xml中的profile的内容如下:
<profiles> <profile> <id>obfuscate</id> <activation> <activeByDefault>false</activeByDefault> </activation> <pluginRepositories> <pluginRepository> <id>pentaho-releases2</id> <url>http://repository.pentaho.org/artifactory/repo/</url> </pluginRepository> </pluginRepositories> <build> <plugins> <plugin> <groupId>com.github.wvengen</groupId> <artifactId>proguard-maven-plugin</artifactId> <version>2.0.6</version> <executions> <execution> <phase>package</phase> <goals> <goal>proguard</goal> </goals> </execution> </executions> <configuration> <target>1.7</target> <encoding>UTF-8</encoding> <obfuscate>true</obfuscate> <injar>classes</injar> <outjar>obfuscate-classes</outjar> <proguardInclude>${basedir}/proguard.pro</proguardInclude> <options> <option>-dontshrink</option> <option>-dontoptimize</option> <!-- 保存jar directories --> <option>-keepdirectories</option> <!-- 保留注解 --> <option>-keepattributes **</option> </options> <assembly> <inclusions> <inclusion> <groupId>com.projects.synergy</groupId> <artifactId>synergy-common</artifactId> </inclusion> <inclusion> <groupId>com.projects.synergy</groupId> <artifactId>synergy-system</artifactId> </inclusion> <inclusion> <groupId>com.projects.synergy</groupId> <artifactId>synergy-wfmservice</artifactId> </inclusion> </inclusions> </assembly> <libs> <lib>${java.home}\lib\rt.jar</lib> <lib>${java.home}\lib\jsse.jar</lib> </libs> </configuration> <dependencies> <dependency> <groupId>net.sf.proguard</groupId> <artifactId>proguard</artifactId> <version>4.9</version> <scope>runtime</scope> </dependency> </dependencies> </plugin> <!-- 使用target\${project.build.finalName}文件夹下的内容进行压缩,避免修改源文件--> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>yuicompressor-maven-plugin</artifactId> <version>1.4.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>compress</goal> </goals> </execution> </executions> <configuration> <encoding>UTF-8</encoding> <!-- 不显示js文件中的错误信息 --> <jswarn>false</jswarn> <!-- 不带压缩后缀 --> <nosuffix>true</nosuffix> <!-- 只压缩,不混淆 --> <nomunge>false</nomunge> <!-- js文件的目录,默认为src\main\js --> <sourceDirectory>${project.build.directory}\${project.build.finalName}</sourceDirectory> <!-- 不包括没有被压缩的文件 --> <excludes> <exclude>**/*min.js</exclude> <exclude>**/*min.css</exclude> </excludes> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.7</version> <executions> <execution> <id>prepare-package-antrun</id> <phase>prepare-package</phase> <configuration> <!--在控制台提示输入当前的版本号信息,并根据输入的版本号替换jsp页面中相关版本信息--> <target name="get new version"> <input addproperty="new.verion" defaultvalue="1.0.0">please input the new project version,default version is : </input> <!-- 创建临时文件存储输入的版本号信息 --> <propertyfile file="${project.build.directory}\version.properties"> <entry key="new.verion" value="${new.verion}"/> </propertyfile> <!-- 替换掉整个项目中jsp页面中的版本号 --> <replaceregexp match="COMPANY V\d+(\.\d+)*" replace="COMPANY V${new.verion}" flags="g" encoding="utf-8"> <fileset dir="${basedir}" includes="**/*.jsp *.jsp"/> </replaceregexp> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <phase>package</phase> <configuration> <!-- 所有的ant任务必须在target中执行 --> <target name="replace js file content and rename aggregate file."> <!-- 替换js文件中使用ajax加载js文件的操作 --> <replaceregexp match="UtilMisc\.loadJS\(\S*\);" replace="" flags="g" encoding="utf-8"> <fileset dir="${project.build.directory}\${project.build.finalName}" includes="**/*.js *.js"/> </replaceregexp> <!-- 合并js文件,删除合并前的js子文件--> <concat destfile="${project.build.directory}\${project.build.finalName}\resources\general\core\adstaffairs\synergy.adstaffairs.js" encoding="utf-8" outputencoding="utf-8" fixlastline="true" append="true"> <fileset dir="${project.build.directory}\${project.build.finalName}\resources\general\core\adstaffairs" includes="**/*.js *.js" excludes="synergy.adstaffairs.js"/> </concat> <delete dir="${project.build.directory}\${project.build.finalName}\resources\general\core\adstaffairs" includeemptydirs="true" includes="**/*.js *.js" excludes="synergy.adstaffairs.js"/> <concat destfile="${project.build.directory}\${project.build.finalName}\resources\general\core\docmanage\synergy.docmanage.js" encoding="utf-8" outputencoding="utf-8" fixlastline="true" append="true"> <fileset dir="${project.build.directory}\${project.build.finalName}\resources\general\core\docmanage" includes="**/*.js *.js" excludes="synergy.docmanage.js"/> </concat> <delete dir="${project.build.directory}\${project.build.finalName}\resources\general\core\docmanage" includeemptydirs="true" includes="**/*.js *.js" excludes="synergy.docmanage.js"/> <!-- 读取版本信息 --> <property file="${project.build.directory}\version.properties"></property> <echo message="war包版本信息->${new.verion}"/> <!-- 替换掉整个项目pom文件中的版本号信息 --> <replaceregexp match="<synergy.version>\d+(\.\d+)*</synergy.version>" replace="<synergy.version>${new.verion}</synergy.version>" flags="g" encoding="utf-8"> <fileset dir="${basedir}\..\" includes="**/pom.xml pom.xml"/> </replaceregexp> <!-- 替换掉target中生成文件中的版本号 --> <move file="${project.build.directory}" todir="" encoding="utf-8" outputencoding="utf-8"> <fileset dir="${project.build.directory}" includes="**/*"> <depth max="1"/> </fileset> <mapper type="glob" from="*-${project.version}.war" to="*-${new.verion}.war"/> </move> <!-- 删除临时文件版本号信息 --> <delete file="${project.build.directory}\version.properties"/> <!-- 删除默认生成的war包,避免和新生成的混淆war包重名 --> <delete file="${project.build.directory}\${project.artifactId}.war"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <!--最终的war包名称不包括版本号信息--> <finalName>${project.artifactId}-obfuscated</finalName> <appendAssemblyId>false</appendAssemblyId> <descriptors> <descriptor>obfuscate_war.xml</descriptor> </descriptors> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> <profile> <id>test</id> <activation> <activeByDefault>false</activeByDefault> </activation> <build> <plugins> <plugin> <groupId>com.juvenxu.portable-config-maven-plugin</groupId> <artifactId>portable-config-maven-plugin</artifactId> <version>1.1.5</version> <executions> <execution> <goals> <goal>replace-package</goal> </goals> </execution> </executions> <configuration> <portableConfig>${basedir}/test.xml</portableConfig> </configuration> </plugin> </plugins> <!-- 指定finalName,否则portable-config-maven-plugin会寻找默认带版本号的war包 --> <finalName>${project.artifactId}</finalName> </build> </profile> <profile> <id>production</id> <activation> <activeByDefault>false</activeByDefault> </activation> <build> <plugins> <plugin> <groupId>com.juvenxu.portable-config-maven-plugin</groupId> <artifactId>portable-config-maven-plugin</artifactId> <version>1.1.5</version> <executions> <execution> <goals> <goal>replace-package</goal> </goals> </execution> </executions> <configuration> <portableConfig>${basedir}/production.xml</portableConfig> </configuration> </plugin> </plugins> <!-- 指定finalName,否则portable-config-maven-plugin会寻找默认带版本号的war包 --> <finalName>${project.artifactId}</finalName> </build> </profile> </profiles>
proguard.pro配置信息
-keeppackagenames **.web,**.entity,**.dao,**.generator,**.timertask,**.interceptor,**.tag,**.system,**.wfmservice,**.common -keep class **.web.**{*;} -keep class **.entity.**{*;} -keep class **.dao.**{*;} -keep class **.timertask.**{*;} -keep class **.interceptor.**{*;} -keep class **.generator.**{*;} -keep class **.tag.**{*;} -keep class **.common.**{*;} -keep class **.system.**{*;} -keep class **.wfmservice.**{*;} -keepclassmembers class **.dao.** { <fields>; <methods>; } -keepclassmembers class **.web.** { <fields>; <methods>; } -keepclassmembers class **.entity.** { <fields>; <methods>; } -keepclassmembers class **.timertask.** { <fields>; <methods>; } -keepclassmembers class **.interceptor.** { <fields>; <methods>; } -keepclassmembers class **.generator.** { <fields>; <methods>; } -keepclassmembers class **.tag.** { <fields>; <methods>; } -keepclassmembers class **.system.** { <fields>; <methods>; } -keepclassmembers class **.common.** { <fields>; <methods>; } -keepclassmembers class **.wfmservice.** { <fields>; <methods>; }
-keeppackagenames:需要保留的包列表。
-keep:需要保留的类。
-keepclassmembers:需要保留的类成员信息。
对于当前项目来说,需要保留类包括实体类(保存与数据库的映射)、Dao类(使用JPA,需要按规则定义方法名)、Controller类(@requestParam等注解根据变量名称映射变量值)以及其它在配置文件中显式声明的类。
<?xml version="1.0" encoding="UTF-8"?> <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> <id>package_project</id> <formats> <format>war</format> </formats> <!-- war包中不包括项目根目录名称 --> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <!-- 不包括当前项目已构建的包 --> <useProjectArtifact>false</useProjectArtifact> <!-- 不包括jar包依赖,使用混淆后的依赖 --> <excludes> <exclude>com.projects.synergy:synergy-common</exclude> <exclude>com.projects.synergy:synergy-system</exclude> <exclude>com.projects.synergy:synergy-wfmservice</exclude> </excludes> <outputDirectory>WEB-INF\lib</outputDirectory> </dependencySet> </dependencySets> <fileSets> <!-- 拷贝当前项目中的混淆的class类 --> <fileSet> <directory>\target\obfuscate-classes\</directory> <outputDirectory>WEB-INF\classes</outputDirectory> <excludes> <exclude>*.jar</exclude> </excludes> </fileSet> <!-- 拷贝当前项目混淆后的依赖类 --> <fileSet> <directory>\target\obfuscate-classes\</directory> <outputDirectory>WEB-INF\lib</outputDirectory> <includes> <include>*.jar</include> </includes> </fileSet> <!-- 从\target\${project.build.finalName}中拷贝出来jar包和classes类之外的内容 --> <fileSet> <directory>\target\${project.build.finalName}\</directory> <outputDirectory></outputDirectory> <excludes> <exclude>\WEB-INF\lib\**</exclude> <exclude>\WEB-INF\classes\**</exclude> </excludes> </fileSet> </fileSets> </assembly>
<profile> <id>test</id> <activation> <activeByDefault>false</activeByDefault> </activation> <build> <plugins> <plugin> <groupId>com.juvenxu.portable-config-maven-plugin</groupId> <artifactId>portable-config-maven-plugin</artifactId> <version>1.1.5</version> <executions> <execution> <goals> <goal>replace-package</goal> </goals> </execution> </executions> <configuration> <portableConfig>${basedir}/test.xml</portableConfig> </configuration> </plugin> </plugins> <!-- 指定finalName,否则portable-config-maven-plugin会寻找默认带版本号的war包 --> <finalName>${project.artifactId}</finalName> </build> </profile>在配置过程中遇到问题总结:
-dontshrink -dontoptimize2. 为了能够保存所有Spring的注解,并且能够使Spring能够扫描到jar中的class类,分别加入以下两条命令。
-keepattributes ** -keepdirectories3. 混淆后的Class类虽然位于不同的包下,但是可能会出现重名的情况,而使用Spring注解,id默认和类名相同,就可能会出现id冲突,因此默认id改为类的完整路径,通过在spring扫描时设置name-generator属性实现。
<context:component-scan base-package="com" name-generator="com.projects.synergy.common.generator.AnnotationFullBeanNameGenerator" > </context:component-scan>AnnotationFullBeanNameGenerator继承默认的注解id生成类AnnotationBeanNameGenerator,并覆盖了默认id生成方法buildDefaultBeanName,内容如下:
package com.projects.synergy.common.generator; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.AnnotationBeanNameGenerator; public class AnnotationFullBeanNameGenerator extends AnnotationBeanNameGenerator { @Override protected String buildDefaultBeanName(BeanDefinition definition) { return definition.getBeanClassName(); } }4. com.github.wvengen.proguard-maven-plugin.2.06.jar在maven中央库中不存在,需要从库:http://repository.pentaho.org/artifactory/repo/中下载,因此需要设置插件库地址。
<!--创建properties文件--> <propertyfile file="${project.build.directory}\version.properties"> <entry key="new.verion" value="${new.verion}"/> </propertyfile> <!--读取properties文件--> <property file="${project.build.directory}\version.properties"></property>13. 现在已经将产品配置文件和混淆的过程分开,分别对应不同的profile,既可以在产品中进行混淆也可以在产品中不混淆。但是在pom.xml中必须将产品的profile放到混淆的profile之后,执行命令时也必须首先声明混淆再声明产品,即clean package -Dmaven.test.skip=true -Pobfuscate -Pproduction。
- JAVA混淆
- JS、CSS文件压缩
- 项目版本号及相关版本信息自动设置
- 不同环境下配置文件修改
<?xml version="1.0" encoding="UTF-8"?> <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> <id>package_project</id> <formats> <format>war</format> </formats> <!-- war包中不包括项目根目录名称 --> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <!-- 不包括当前项目已构建的包 --> <useProjectArtifact>false</useProjectArtifact> <!-- 不包括jar包依赖,使用混淆后的依赖 --> <excludes> <exclude>com.projects.synergy:synergy-common</exclude> <exclude>com.projects.synergy:synergy-system</exclude> <exclude>com.projects.synergy:synergy-wfmservice</exclude> </excludes> <outputDirectory>WEB-INF\lib</outputDirectory> </dependencySet> </dependencySets> <fileSets> <!-- 拷贝当前项目中的混淆的class类 --> <fileSet> <directory>\target\obfuscate-classes\</directory> <outputDirectory>WEB-INF\classes</outputDirectory> <excludes> <exclude>*.jar</exclude> </excludes> </fileSet> <!-- 拷贝当前项目混淆后的依赖类 --> <fileSet> <directory>\target\obfuscate-classes\</directory> <outputDirectory>WEB-INF\lib</outputDirectory> <includes> <include>*.jar</include> </includes> </fileSet> <!-- 从\target\${project.build.finalName}中拷贝出来jar包和classes类之外的内容 --> <fileSet> <directory>\target\${project.build.finalName}\</directory> <outputDirectory></outputDirectory> <excludes> <exclude>\WEB-INF\lib\**</exclude> <exclude>\WEB-INF\classes\**</exclude> </excludes> </fileSet> </fileSets> </assembly>使用portable-config-maven-plugin修改不同环境的配置文件 portable-config-maven-plugin 与 resource filter 对比 :http://fuxueliang.com/tool/2013/08/16/portable-config-maven-plugin-vs-resource-or-war-filter/ portable-config-maven-plugin插件:https://github.com/juven/portable-config-maven-plugin 不同环境中的配置文件修改可以使用 maven本身提供的resource filter和 maven插件portable-config-maven-plugin。 使用resource filter:配置文件中的变量必须使用占位符,maven resource filter会根据占位符替换相应的内容。 使用portable-config-maven-plugin:根据properties文件或xml文件中的关键字替换关键字值的内容,在properties和xml文件中的内容具有默认值。对war文件中的配置文件修改时,war包名称默认为${project.artifactid}-${project.version}.war。
<profile> <id>test</id> <activation> <activeByDefault>false</activeByDefault> </activation> <build> <plugins> <plugin> <groupId>com.juvenxu.portable-config-maven-plugin</groupId> <artifactId>portable-config-maven-plugin</artifactId> <version>1.1.5</version> <executions> <execution> <goals> <goal>replace-package</goal> </goals> </execution> </executions> <configuration> <portableConfig>${basedir}/test.xml</portableConfig> </configuration> </plugin> </plugins> <!-- 指定finalName,否则portable-config-maven-plugin会寻找默认带版本号的war包 --> <finalName>${project.artifactId}</finalName> </build> </profile>在配置过程中遇到问题总结: 1. 不使用proguard插件的压缩(shrink)、代码优化(optimize)、改变包目录结构、Class类内容以及其它修改包内容的命令。因为这些命令虽然能够优化代码,但是优化的过程是不可控的,会产生很多问题,例如:某个方法被删除、class类字节编码验证失败等。因此只加入如下两条不压缩和不优化的两条命令。
-dontshrink -dontoptimize2. 为了能够保存所有Spring的注解,并且能够使Spring能够扫描到jar中的class类,分别加入以下两条命令。
-keepattributes ** -keepdirectories3. 混淆后的Class类虽然位于不同的包下,但是可能会出现重名的情况,而使用Spring注解,id默认和类名相同,就可能会出现id冲突,因此默认id改为类的完整路径,通过在spring扫描时设置name-generator属性实现。
<context:component-scan base-package="com" name-generator="com.projects.synergy.common.generator.AnnotationFullBeanNameGenerator" > </context:component-scan>AnnotationFullBeanNameGenerator继承默认的注解id生成类AnnotationBeanNameGenerator,并覆盖了默认id生成方法buildDefaultBeanName,内容如下:
package com.projects.synergy.common.generator; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.AnnotationBeanNameGenerator; public class AnnotationFullBeanNameGenerator extends AnnotationBeanNameGenerator { @Override protected String buildDefaultBeanName(BeanDefinition definition) { return definition.getBeanClassName(); } }4. com.github.wvengen.proguard-maven-plugin.2.06.jar在maven中央库中不存在,需要从库:http://repository.pentaho.org/artifactory/repo/中下载,因此需要设置插件库地址。 5. 在proguard插件配置中,将依赖的包进行了混淆,被混淆的jar包也被放入obfuscate-classes中。在使用assembly打war包时,首先将相应的原有maven依赖包移除,然后分别将obfuscate-classes中内容放到WEB-INF\classes和WEB-INF\lib下。 6. yuicompressor-maven-plugin插件的aggregation任务中,如果output设置的输出文件名和项目以后的文件重名,会首先删除项目中文件,然后再重新建一个新文件。因此,需要给output文件名加后缀“-obfuscate”,以做区分。在压缩完成后在使用ant命令重命名加后缀“-obfuscate”的文件。 7. yuicompressor-maven-plugin插件的aggregation任务中,设置<removeIncluded>true</removeIncluded>会将include中包含的文件删掉,因此聚合的inputDir不要设置源文件目录。 8. yuicompressor使用的sourceDirectory为target\ ${project.build.finalName},直接对文件夹中的js文件进行压缩,exclude的文件在压缩完成之后也包括在当前文件夹下,不受yuicompressor压缩的影响。因此assembly打包时,可以直接拷贝当前文件夹下的内容。 9. yuicompressor-maven-plugin插件的aggregation任务中,会根据include中文件匹配顺序合并js文件,由于js存在命名空间的问题,因此根命名空间要合并到文件前面。 10. maven-antrun-plugin能够在maven中运行ant的任务,包括文件目录操作、文件内容替换、文件删除和重命名等,但是所有的任务必须放到<target>标签中。在执行时maven会根据target中任务新建一个ant xml文件。 11. 在使用yuicompressor-maven-plugin进行js文件合并时,在eclipse中执行maven控制台命令,合并后的js文件没有乱码,在Windows控制台中执行maven控制台命令会出现乱码。通过设置maven资源编码方式、JDK编码方式和yuicompressor-maven-plugins的编码方式为utf-8都不起作用。所以直接抛弃使用yui进行js压缩的方式,改用antrun进行js文件合并。 12. 在maven-antrun-plugin的一个execution阶段输入的properties属性,在其它的execution阶段不能被访问到。解决方式为:将需要被访问的properties属性放到一个antrun创建的一个共享文件中。如下:
<!--创建properties文件--> <propertyfile file="${project.build.directory}\version.properties"> <entry key="new.verion" value="${new.verion}"/> </propertyfile> <!--读取properties文件--> <property file="${project.build.directory}\version.properties"></property>13. 现在已经将产品配置文件和混淆的过程分开,分别对应不同的profile,既可以在产品中进行混淆也可以在产品中不混淆。但是在pom.xml中必须将产品的profile放到混淆的profile之后,执行命令时也必须首先声明混淆再声明产品,即clean package -Dmaven.test.skip=true -Pobfuscate -Pproduction。 14. 在windows系统下可以直接使用控制台的命令进行maven打包。可以将所有的maven打包命令放到一个bin文件夹下,然后将bin文件夹放到项目目录下。运行bin文件夹下的相应命令执行打包。bin文件夹见附件。