Safety-1

快速实现

本章将按照上一章的设计思路进行一个快速实现。

快速构架

首先我们定义加解密器的接口:

public interface SafetyCipher {

	Object encrypt(Object value);

	Object decrypt(Object value);

}

这里我们先以Base64 为例,做一个实现并注册为Spring Bean:

@Component
public class Base64Cipher implements SafetyCipher {

	private static final Base64.Encoder encoder = Base64.getEncoder();

	private static final Base64.Decoder decoder = Base64.getDecoder();

	public Object encrypt(Object value) {
		String str = transToString(value);
		return encoder.encodeToString(str.getBytes(StandardCharsets.UTF_8));
	}

	public Object decrypt(Object value) {
		String str = transToString(value);
		return new String(decoder.decode(str), StandardCharsets.UTF_8);
	}

	private String transToString(Object value) {
		if (value instanceof String) {
			return (String) value;
		}
		return String.valueOf(value);
	}
}

然后在快速实现拦截器,加密拦截器如下:

/**
* 这里我们拦截select和update方法,将参数进行加密
*/
@Component
@Intercepts({
		@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
		@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
		@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class EncryptInterceptor implements Interceptor {

	@Autowired
	private SafetyCipher safetyCipher;

	public Object intercept(Invocation invocation) throws Throwable {
		Object param = invocation.getArgs()[1]; // 直接获取参数
		safetyCipher.encrypt(param);
		return invocation.proceed();
	}
}

解密拦截器:

/**
* 相对加密拦截,解密就单纯很多,直接拦截返回结果进行解密即可
*/
@Component
@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}))
public class DecryptInterceptor implements Interceptor {

	@Autowired
	private SafetyCipher safetyCipher;

	@SuppressWarnings("unchecked")
	public Object intercept(Invocation invocation) throws Throwable {
		List<Object> rows = (List<Object>) invocation.proceed();
		safetyCipher.decrypt(rows);
		return rows;
	}
}

这里我们的基础实现就完成了,显然这离我们的目标还差很多,但我们是快速实现,先构建主体运行流程,之后在进行细节上的补充。

注解补充

我们期望的是一个通过注解来实现字段级的加解密配置,那么我们还应该定义一个作用于实体字段的注解,并且应该在加解密的时候根据注解进行加解密的控制,对没有进行注解标注的字段不进行处理,下面我们就来一步步完成。

首先,定义一个注解

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Safety {
}

然后分别对加解密拦截器进行改造,先改造解密拦截器,通过反射获取到返回实体的字段信息,然后对标有注解的字段进行解密处理:

public class DecryptInterceptor implements Interceptor {
  
	...
    
	@SuppressWarnings("unchecked")
	public Object intercept(Invocation invocation) throws Throwable {
		List<Object> rows = (List<Object>) invocation.proceed();
		rows.parallelStream().forEach(this::decrypt);
		return rows;
	}

	private void decrypt(Object row){
		MetaObject object = SystemMetaObject.forObject(row);
		Field[] fields = row.getClass().getDeclaredFields();
		Arrays.stream(fields)
				.filter(field -> field.isAnnotationPresent(Safety.class))
				.forEach(field -> doDecrypt(field, object));
	}

	private void doDecrypt(Field field, MetaObject object){
		String name = field.getName();
		Object value     = object.getValue(name);
		object.setValue(name, safetyCipher.decrypt(value));
	}

}

接着同样,我们对加密进行修改

public class EncryptInterceptor implements Interceptor {

	...

	public Object intercept(Invocation invocation) throws Throwable {
		Object     param  = invocation.getArgs()[1];
		MetaObject meta   = SystemMetaObject.forObject(param);
		Field[]    fields = param.getClass().getDeclaredFields();
		Arrays.stream(fields)
				.filter(field -> field.isAnnotationPresent(Safety.class))
				.forEach(field -> doEncrypt(field, meta));
		return invocation.proceed();
	}

	private void doEncrypt(Field field, MetaObject meta) {
		String name  = field.getName();
		Object value = meta.getValue(name);
		if (Objects.isNull(value)) return;
		meta.setValue(name, safetyCipher.encrypt(value));
	}
}

这样我就基本完成了基于注解的加解密实现了,但是这仍不完善,通常我们使用mybatis往往不会使用完整实体进行查询或者返回结果也可能是Map而非实体,后面我们在一步步的解决完善。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值