java:服务端字段保护机制的设计思路/实现及总结

背景

最近在做新的服务端设计时,愈发感受到信息安全在实际应用中的重要性。

我们的项目中涉及大量用户信息。这些信息在应用中有的不能对外暴露,有时需要完整提供给第三方,有时只能提供脱敏信息,有时又需要根据实际请求类型来决定是否脱敏,有的数据库字段不允许用户写入。

如果完全按以前的逻辑,需要针对每个不同的场景设计不同的逻辑,这些逻辑大部分又是类似或相同的。服务端由多个成员设计,对逻辑的理解程序又可能参差不齐,最终产品的设计质量也会不同.

唉,越想越复杂,头疼。

穷则思变,难也会思变。

面对这越来越复杂的逻辑,我开始考虑设计一个独立的字段保护机制。将这些通用需求抽象化,设计为更简单更方便的形式提供给应用层实现这些逻辑控制。

其实在项目的上一个版本,我设计了一个基于正则表达定义过滤规则的字段过滤器机制,实际应用中考虑到正则表达式对于大多数程序员来说实在太过复杂,并没有真正应用到业务场景。

需求定义

下面就要进一步分析,我们到底有哪些字段保护需求呢?主要分为静态字段过滤动态字段过滤.

Java Bean静态字段保护

比如下面的表中private_time字段,它用于存储设备令牌的创建时间,用于服务端计算令牌的关键数据,对于服务端来说是绝密,不允许让服务端之外的地方知道.那这表记录在发送给客户端时就不应该出现这个字段.同时也不允许客户端修改这个字段.

CREATE TABLE IF NOT EXISTS dc_device (
  `id`             int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'X@NAME:设备id@X',
  `name`           varchar(32) DEFAULT NULL COMMENT 'X@NAME:设备名称@X,用户指定',
  `physical_address` varchar(32) NOT NULL UNIQUE COMMENT '设备X@NAME:物理地址@X,MAC地址,IMEI或其他设备识别码',
  `private_time`     bigint DEFAULT 0 COMMENT 'SCOPE@LOCAL@EPOSC设备令牌创建的时间戳(毫秒),每次创建设备令牌都会修改此字段',
  `os_arch`        varchar(64) DEFAULT NULL COMMENT 'X@NAME:操作系统平台@X,操作系统名称及版本及硬件架构名称,例如:Windows-x86_64,Linux-x86_64,Android-arm...'
) COMMENT 'X@NAME:前端设备记录@X,前端设备基本信息' DEFAULT CHARSET=utf8;

在这种情况下,我们需要将这些需要被保护的字段在代码编译期就写死,不允许出现在网络传输的数据中.对于这种需求,我们称之为静态字段过滤.

Java Bean动态字段保护

静态字段过滤因为在编译期就写死了,不能被修改,所以安全性上是保证了字段不能被泄露出去,带来的缺点也是明显的,就是不够灵活.

所以为了服务运行时不同服务方法的个性化定制需求,还需要设计动态字段保护机制,允许针对不同服务方法制定不同的字段保护策略

字段过滤

动态字段过滤与静态字段保护的结果是一样的,就是让保护字段不出现在与客户端交互的数据流中.

比如在字段安全保护场景,将不允许用户修改的字段(balance)在作为服务方法输入参数反序列化时忽略处理,避免无权限的客户端远程修改该字段。

值过滤

在数据对象序列化/反序列化时替换指定的字段的值就是值过滤

比如在隐私保护场景,将用户名字段 (name)在作为服务方法结果输出时加*号显示为王*华,就是修改了输出给客户端的数据对象的name的值.

序列化和反序列化

不论是静态字段保护还是动态字段保护都是通过作用在Java Bean数据对象的序列化或反序列化两个阶段实现的.

不论是Thrift RPC还是SpringWeb服务,服务方法的输入和输出参数都要通过网络在Server/Client之间传输。实现数据对象传输,发送端需要对数据对象进行序列化(JSON或二进制数据流),接收端需要对收到的数据反序列化还原为原始的数据对象。

服务方法接收到参数是输入,需要经过反序列化.

服务方法的返回结果是输出,则要经过序列化.

静态字段保护作用时不区分序列化和反序列化,一刀切,要保护就两个方向都保护.

但是动态字段保护可以指定保护的方向,可以要求只作用于序列化,或只作用于反序列化,也可以作用两个方向.

实现

前阵子通过对Spring-core注解工具的深入学习和解构。我对注解(Annotation)的设计和使用有了更丰富的经验。

为此我还写过相关的博客:

《java:从spring-core移植的注解(annotation)扫描工具模组common-annotutils(适用JDK 1.7)》

《spring-core:理解@AliasFor注解的作用》

《spring-core:注解扫描工具MergedAnnotations获取复合注解上的元注解》

《spring-core:注解扫描工具AnnotatedElementUtils.hasMetaAnnotationTypes方法说明》

《spring-core:注解合成(AnnotationUtils.synthesizeAnnotation)的使用示例》

《spring-core:获取类/方法/字段/字段上直接定义的注解》

开发团队成员也已经适应了Spring应用开发中大量注解的应用。所以决定基本的设计思路:设计一套独立的基于注解的服务端字段过滤机制。

静态字段保护实现

注解(Annotation)实现

Spring Web的数据序列化和反序列化是基于Jackson来实现的,Thrift 有自己的序列化和反序列实现.

可以在字段上定义注解@JsonIgnore可以实现Jackson在序列化和反序列化时忽略该字段.

而Thrift Struct如果在字段setter/getter方法不定义@ThriftField注解,该字段就会在序列化和反序列化时被忽略.

所以不论是对是SpringWEB服务还是Thrfit RPC服务,实现静态的字段保护就是控制字段上的对应的注解定义.这很好理解.

比如下面的Java Bean定义,privateInfo字段定义了@JsonIgnore,同时getter/setter方法也没有定义@ThriftField注解,privateInfo在Thrift RPC和Spring WEB服务端与客户端交互时就会被忽略

import com.alibaba.fastjson.annotation.JSONField;
import com.facebook.swift.codec.ThriftField;
import com.fasterxml.jackson.annotation.JsonIgnore;

public final class UserBean {
	
	private String name;
	private String id;
	@JSONField(serialize = false,deserialize = false)
	@JsonIgnore
	private String privateInfo;
	public UserBean() {
	}
	@ThriftField(0)
	public String getName() {
		return name;
	}
	@ThriftField(name="name")
	public void setName(String name) {
		this.name = name;
	}
	@ThriftField(1)
	public String getId() {
		return id;
	}
	@ThriftField(name="id")
	public void setId(String id) {
		this.id = id;
	}
	public String getPrivateInfo() {
		return privateInfo;
	}
	public void setPrivateInfo(String privateInfo) {
		this.privateInfo = privateInfo;
	}
}
sql2java

sql2java,因为数据库访问对象类的代码都是自动生成的,没办法如上手工修改,从3.32.0版本开始,sql2java增加了静态字段过滤功能,在生成sql2java的数据库表记录对象类时,允许指定字段的可见度(ColumnVisibility)从而自动在生成的代码实现上述的注解定义。

详细说明参见我的上一篇博客 《java:sqlj2ava的静态字段保护》

动态字段保护实现

关于动态字段保护的实现,相比静态字段保护要更复杂.

涉及注解(Annotation)设计、服务请求拦截、定制Jackson,fastjson的序列化/反序列化器、过滤器注入等待环节.为此我设计了一个独立的动态字段过滤工具:beanfilter,已经向maven发布了第一个版本,还不完善,后续会持续更新,如下为代码仓库位置:

beanfilter: 基于注解(Annotation)实现的服务端(spring/thrift)对JavaBean类型数据在序列化和反序列化阶段动态字段过滤(IFieldFilter)和值过滤(IValueFilter)工具。 (gitee.com)

关于beanfilter的项目介绍可以先阅读我之前的博客,再进一步看项目说明:

《我的开源工具beanfilter:实现基于注解(Annotation)的服务端(spring)动态字段过滤》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值