Java代码混淆和JSON数据序列化问题
基本观点:
第一点:Java代码生成的Jar文件很容易被反编译,常用工具JD-GUI(http://jd.benow.ca/)
第二点:Jar文件常用的“加密”方式是混淆(有专门的混淆软件,有些需要收费的),使得类名、类中字段和方法名称等难以肉眼判断。例如
第三点:JSON协议使用方便,非常流行。常用框架由于可以使用自定义Model类,直接转换成JSON格式的数据,使用起来方便快捷。我使用的alibaba的fastjson.jar。(吐槽一下:虽然该包有不少bug需要进一步完善,整体已经相当不错了。) 例如:
基本的Model
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试:
import com.alibaba.fastjson.JSONObject;
public class Test {
public static void main(String[] args) {
String json = JSONObject.toJSONString(new User("wsc", 25));
System.out.println(json);
User user = JSONObject.parseObject(json, User.class);
System.out.println(user.getAge());
System.out.println(user.getName());
}
}
打印:
{"age":25,"name":"wsc"}
25
wsc
第四点:
当使用框架转换Model为String类型的JSON数据时,通常是根据Model的set和get方法获取需要保存到磁盘的字段名称,常见手段是反射,如果直接获取Field,可能会产生很多,包括某些Field其实用户不想保存到磁盘transient字段或者中间变量字段,尽管fastjson.jar使用了字节码框架ASM来提高效率,也是需要通过set/get方法获取到字段名称。
问题:
如果使用了混淆,代码中字段和方法名称都被混淆,JavaBean的规则被改写,确实是能反序列化出对象,但是可能User名称已经被更改为AA,属性Name可能被更改为f.这个时候就会出现异常。
解决办法:
方法1:有些人推荐set和get方法不适用混淆,相当于Model的结构是公开的,虽然能够避免,总体不安全。
方法2:JSON序列化还有另外一种机制,Model是Map类型
Map是直接获取字段名称的。
使用方法2的缺点是:当你反序列化的时候,如果你的Model里字段类型是自定义类对象,比如User对象中Age不是String,而是自定义类型Age,它是无法帮你自动反序列化成自定义对象Age的,需要手动创建。
举例:
AppCheckBasic是一个基本Model,集成了JSONObject,由于JOSNObject本身是Map结构,所以AppCheckBasic也是一个Map。
AppCheckBasic有一个属性apps,它是List 类型。
public class AppCheckBasic extends JSONObject {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unchecked")
public void initial() {
// 把所有层次结构初始化
List apps = null;
Object obj = this.get("apps");
// 初始化赋值后使用
if (obj instanceof ArrayList) {
apps = (List) obj;
}
// 反序列化(非第一次初始化后)
if (obj instanceof JSONArray) {
JSONArray array = (JSONArray) this.get("apps");
apps = new ArrayList();
for (int i = 0; i < array.size(); i++) {
// AppBasic内部没有"复杂"数据类型
AppBasic appBasic = JSON.parseObject(array.getString(i),
AppBasic.class);
apps.add(appBasic);
}
}
// 初始化内容
this.put("apps", apps);
}
// set和get方法是为了方便
@SuppressWarnings("unchecked")
public List getApps() {
List apps = (List) this.get("apps");
return apps;
}
public void setApps(List apps) {
this.put("apps", apps);
}
}
除了必须手动反序列化重建自定义对象或者复杂对象如List等,为了方便为该Map类型的Bean定义set和get方法。