java 防止反射_Java设计模式(一):单例模式,防止反射和反序列化漏洞

一、懒汉式单例模式,解决反射和反序列化漏洞

package com.iter.devbox.singleton;

import java.io.ObjectStreamException;

import java.io.Serializable;

/**

* 懒汉式(如何防止反射和反序列化漏洞)

* @author Shearer

*

*/

public class SingletonDemo6 implements Serializable{

// 类初始化时,不初始化这个对象(延迟加载,真正用的时候再创建)

private static SingletonDemo6 instance;

private SingletonDemo6() {

// 防止反射获取多个对象的漏洞

if (null != instance) {

throw new RuntimeException();

}

}

// 方法同步,调用效率低

public static synchronized SingletonDemo6 getInstance() {

if (null == instance)

instance = new SingletonDemo6();

return instance;

}

// 防止反序列化获取多个对象的漏洞。

// 无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。

// 实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。

private Object readResolve() throws ObjectStreamException {

return instance;

}

}

package com.iter.devbox.singleton;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

public class Client2 {

public static void main(String[] args) throws Exception {

SingletonDemo6 sc1 = SingletonDemo6.getInstance();

SingletonDemo6 sc2 = SingletonDemo6.getInstance();

System.out.println(sc1); // sc1,sc2是同一个对象

System.out.println(sc2);

// 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞)

/*Classclazz = (Class) Class.forName("com.iter.devbox.singleton.SingletonDemo6");

Constructorc = clazz.getDeclaredConstructor(null);

c.setAccessible(true); // 跳过权限检查

SingletonDemo6 sc3 = c.newInstance();

SingletonDemo6 sc4 = c.newInstance();

System.out.println(sc3); // sc3,sc4不是同一个对象

System.out.println(sc4);*/

// 通过反序列化的方式构造多个对象(类需要实现Serializable接口)

// 1. 把对象sc1写入硬盘文件

FileOutputStream fos = new FileOutputStream("object.out");

ObjectOutputStream oos = new ObjectOutputStream(fos);

oos.writeObject(sc1);

oos.close();

fos.close();

// 2. 把硬盘文件上的对象读出来

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out"));

// 如果对象定义了readResolve()方法,readObject()会调用readResolve()方法。从而解决反序列化的漏洞

SingletonDemo6 sc5 = (SingletonDemo6) ois.readObject();

// 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。

System.out.println(sc5);

ois.close();

}

}

二、静态内部类式单例模式(解决反射和反序列化漏洞)

package com.iter.devbox.singleton;

import java.io.ObjectStreamException;

import java.io.Serializable;

/**

* 静态内部类实现方式(也是一种懒加载方式)

* 这种方式:线程安全,调用效率高,并且实现了延迟加载

* 解决反射和反序列化漏洞

* @author Shearer

*

*/

public class SingletonDemo7 implements Serializable{

private static class SingletonClassInstance {

private static final SingletonDemo7 instance = new SingletonDemo7();

}

// 方法没有同步,调用效率高

public static SingletonDemo7 getInstance() {

return SingletonClassInstance.instance;

}

// 防止反射获取多个对象的漏洞

private SingletonDemo7() {

if (null != SingletonClassInstance.instance)

throw new RuntimeException();

}

// 防止反序列化获取多个对象的漏洞

private Object readResolve() throws ObjectStreamException {

return SingletonClassInstance.instance;

}

}

package com.iter.devbox.singleton;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.lang.reflect.Constructor;

public class Client3 {

public static void main(String[] args) throws Exception {

SingletonDemo7 sc1 = SingletonDemo7.getInstance();

SingletonDemo7 sc2 = SingletonDemo7.getInstance();

System.out.println(sc1); // sc1,sc2是同一个对象

System.out.println(sc2);

// 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞)

Classclazz = (Class) Class.forName("com.iter.devbox.singleton.SingletonDemo7");

Constructorc = clazz.getDeclaredConstructor(null);

c.setAccessible(true); // 跳过权限检查

SingletonDemo7 sc3 = c.newInstance();

SingletonDemo7 sc4 = c.newInstance();

System.out.println("通过反射的方式获取的对象sc3:" + sc3); // sc3,sc4不是同一个对象

System.out.println("通过反射的方式获取的对象sc4:" + sc4);

// 通过反序列化的方式构造多个对象(类需要实现Serializable接口)

// 1. 把对象sc1写入硬盘文件

FileOutputStream fos = new FileOutputStream("object.out");

ObjectOutputStream oos = new ObjectOutputStream(fos);

oos.writeObject(sc1);

oos.close();

fos.close();

// 2. 把硬盘文件上的对象读出来

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out"));

// 如果对象定义了readResolve()方法,readObject()会调用readResolve()方法。从而解决反序列化的漏洞

SingletonDemo7 sc5 = (SingletonDemo7) ois.readObject();

// 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。

System.out.println("对象定义了readResolve()方法,通过反序列化得到的对象:" + sc5);

ois.close();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值