Java使用序列化的私有方法巧妙解决部分属性持久化问题
部分属性持久化问题看似很简单,只要把不需要的持久化的属性加上瞬态关键字(transient关键字)即可,没错,这也是一种解决方案,但在有的时候行不通,例如在一个计税系统和人力系统对接的时候,计税系统需要从人力系统获得人员的姓名和基本工资,作为纳税的一句,而人力系统的工资分成 分成两个部分:基本工资和绩效工资,基本工资没有什么秘密,一般都是直接跟年限挂钩,但是绩效工资一般来说是保密的,不能泄露到外系统,话不多说,上代码
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import java.io.Serializable; @Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Salary implements Serializable {
private static final long serialVersionUID = 75632L;
// 基本工资
private Integer basePay;
// 绩效工资
private Integer bonus;
}
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import java.io.Serializable; @Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private static final long serialVersionUID =45829L;
// 员工姓名
private String name;
// 员工薪资
private Salary salary; }
如上所示,他们都序列化了,都基本了持久化的条件,计税系统请求人力系统,然后人力系统后将人员和工资信息传递到计税系统中,做一个测试,代码如下:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
@RequestMapping("/demo")
public class UserController { @GetMapping("/get-user")
public Person getUser() {
// 基本工资 1000 绩效3000
Salary salary = new Salary(1000,3000);
Person person = new Person("小哇",salary);
return person;
}
}
在通过网络传输的计税系统中,进行反序列化,代码如下:
import com.cp.security.user.common.JsonUtil;
public class TestController {
public static void main(String[] args) {
String personJson = HttpRequest.sendGet("http://localhost:8080/demo/get-user","");
Person person = JsonUtil.str2Obj(personJson);
StringBuffer sb = new StringBuffer();
sb.append("name"+person.getName());
sb.append("基本工资"+person.getSalary().getBasePay());
sb.append("绩效工资"+person.getSalary().getBonus());
System.out.println(sb);
}
//运行结果如下:
// name 小哇 基本工资1000 绩效工资3000
很明显这不符合要求,存在严重的信息泄露问题,那么怎么解决呢?提供以下思路:
1.在bonus(绩效工资)前面加上transient关键字进行修饰
这的确是一个办法,但不是一个好的办法,在分布式部署的时候性能很差,不推荐
2.新增一个业务对象(*)
新增一个对象,该对象只有姓名和基本工资两个属性,符合开闭原则,对原来系统也没有侵入性,只是增加了工作量,相比较来说,这个是目前最受欢迎的方式了
3.在请求端进行过滤
在计税系统得到Person对象之后,对立面的绩效薪资进行隐藏,可行但是很危险,业务交由其他系统处理,对外依旧是暴露的,差评
4.引出本文重点,采用Serializable接口中的两个私有方法writeObject和readObject,我们将Person稍作修改,上代码
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; @Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private static final long serialVersionUID =45829L;
// 员工姓名
private String name;
// 员工薪资
private Salary salary; private void writeObject(ObjectOutputStream out)throws IOException {
out.defaultWriteObject();
out.writeInt(salary.getBasePay());
}
private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException{
in.defaultReadObject();
salary = new Salary(in.readInt(),0);
}
}
// 此时运行的结果
// name 小哇 基本工资1000 绩效工资0
总结:
我们在Person类中增加了writeObject和readObject两个方法,并且访问权限都是私有级别的,为什么能够改变程序的运行结果呢?其实这里使用了序列化的独有机制,序列化毁掉,java中调用ObjectOutputStream类把一个对象转换成流数据时,会通过反射检查被序列化的类是否有writeObject方法,并且检查其是否符合私有、无返回的特性,若符合,则会委托该方法将对象进行序列化,若没有,则由ObjectOutputStream按照默认的规则继续序列化,同样,从流数据恢复成实力对象时,也会检查是否有一个私有的readObject方法,如果有,则会通过该方法读取属性值,此处有几个关键点需要说明:
a> out.defaultWriteObject();
告知JVM按照默认的规则写入对象,惯例写法是写在第一句话里
b>in.defaultReadObject();
告知JVM按照默认的规则读入对象,惯例写法是写在第一句话里
c>依然存在分布式部署的问题,只是提供一个思路而已,正常情况下依然推荐使用第二种方式,重新声明一个对象返回
Java使用序列化的私有方法巧妙解决部分属性持久化问题的更多相关文章
- Java常见序列化与反序列方法总结
很多商业项目用到数据库.内存映射文件和普通文件来完成项目中的序列化处理的需求,但是这些方法很少会依靠于Java序列化.本文也不是用来解释序列化的,而是一起来看看面试中有关序列化的问题,这些问题你很有可 ...
- java中用反射访问私有方法和私有成员[转]
转自: http://zhouyangchenrui.iteye.com/blog/470521 java的反射可以绕过访问权限,访问到类的私有方法和成员.可能这点会引起安全性的讨论.反射的使用帮助解 ...
- Java反射机制调用私有方法
1.获取目标类: 每个类都有一个class属性,通过实体类的class属性获取: Class clazz = Person.class 通过对象获取. Person p1 = new Person( ...
- 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)
编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...
- java基础 接口私有方法
/** * 问题描述: * 我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题 * 但是这个共有方法不应该让实现类使用,应该是私有化的. * * 解决方案: * 从java 9开始,接口 ...
- 我要告诉你:java接口中可以定义private私有方法
在传统的Java编程中,被广为人知的一个知识点是:java Interface接口中不能定义private私有方法.只允许我们定义public访问权限的方法.抽象方法或静态方法.但是从Java 9 开 ...
- Python3学习之路~6.5 私有属性和私有方法
属性分为静态属性和动态属性,静态属性就是变量,动态属性就是方法.但是一般我们说的属性就是变量,方法就是方法.私有属性/方法就是外面访问不了,只有自己能够访问的属性/方法.如何将属性和方法分别变成私有属 ...
- js——private 私有方法公有化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Python中的私有属性私有方法、类属性类方法以及单例设计模式
私有属性是对象不希望公开的属性,私有方法是对象不希望公开的方法.在定义私有属性和私有方法时,在属性或者方法前,加上__(两个下划线) 公有方法可以通过对象名直接调用,私有方法不能通过对象名直接调用,只 ...
随机推荐
- Visual Studio UML类图
1.unified Modeling Language(UML)称为同一建模语言或者标准建语言, 用例图:对系统的使用方式的分类.类图:显示类和他们的相互关系. 对象图:只显示对象及他们的相互关系. ...
- 2017-2018-2 20165314实验二《Java面向对象程序设计》实验报告
实验报告封面 实验一 实验要求 参考 http://www.cnblogs.com/rocedu/p/6371315.html#SECUNITTEST 完成单元测试的学习提交最后三个JUnit测试用例 ...
- 解决Xshell不从22端口连接服务器
xshell默认是22端口 如果服务器给的ssh端口不是22,会连接失败 需要去指定连接 新建 设置ip和端口,点下面的确定 双击刚创建的会话 输入用户名密码 连接成功
- jmeter IP欺骗功能
使用过loadrunner的同学,应该都了解有个IP欺骗功能,jmeter遇到类似需求怎样实现呢? 环境:windows7,jdk1.8,jmeter3.1 使用IP欺骗功能前提是本地有多个可用IP, ...
- Python面向对象 三大特性 综合案例+1(视频里的作业)
class Dog: # 在创建一个小狗实例的时候,给它设置几个属性 def __init__(self, name, age = 1): self.name = name self.age = ag ...
- (转)HTTPS到底是个啥玩意儿?
详细见:https://blog.csdn.net/zgwangbo/article/details/50889623 ,建立交互的过程见下图:
- gotty---用来作为k8s的web terminal,通过参数读取指定pod的日志输出
不要重复造轮子,我觉得这个方案比较适合现在的情况. 我们知道,如果手工查看k8s里指定pod的日志输出,一般的方案如下: kubectl logs -f -n [namespace] [pod] -c ...
- Vue报错:Uncaught TypeError: Cannot assign to read only property’exports‘ of object’#<Object>‘的解决方法
发现问题 运行一下以前的一个Vue+webpack的 vue仿新闻网站 小项目,报错 由于自己vue学习不深入,老是这个报错,找了好久(确切的说是整整一下午^...^)才找到原因 -v- Uncau ...
- OAuth2:隐式授权(Implicit Grant)类型的开放授权
适用范围 仅需临时访问的场景 用户会定期在API提供者那里进行登录 OAuth客户端运行在浏览器中(Javascript.Flash等) 浏览器绝对可信,因为该类型可能会将访问令牌泄露给恶意用户或应用 ...
- 【AtCoder】Tenka1 Programmer Contest 2019
Tenka1 Programmer Contest 2019 C - Stones 题面大意:有一个01序列,改变一个位置上的值花费1,问变成没有0在1右边的序列花费最少多少 直接枚举前i个都变成0即 ...