包含内容
- 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中对应的内容,否则不执行。
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。