在我们日常的业务需求中,经常会遇到需要对存储的用户敏感数据进行加密处理的场景,如用户的身份信息、住址、身份证号等等,本文我们就讨论下,业务系统(后端)如何实现数据存储(基于MySQL)的加解密功能。

技术栈:springboot、mybatis、mysql等

方案一:基于spring aop拦截mybatis mapper.

第一步:定义注解@Encrypt

@Target(ElementType.METHOD)//注解的范围是类、接口、枚举的方法上
@Retention(RetentionPolicy.RUNTIME)//被虚拟机保存,可用反射机制读取
public @interface Encrypt{/*** 入参需要加密的字段* @return*/String[] paramFields() default {};/*** 响应参数需解密的字段* @return*/String[] respFields() default {};
}

第二步:开发拦截器处理类

@Slf4j
@Aspect
@Component
public class EncryptAspect {@Pointcut("@annotation(com.xxx.annotation.Encrypt)")public void encryPointCut() {}@Around("encryPointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();final Sm4Intercept annotation = method.getAnnotation(Encrypt.class);if (null == annotation) {return joinPoint.proceed();}//加密入参对象属性值encryptRequest(joinPoint, annotation);//执行目标方法Object response = joinPoint.proceed();//解密响应结果对象的属性值decryptResponse(response, annotation);return response;}/*** 加密请求入参对象** @param joinPoint* @param annotation*/private void encryptRequest(ProceedingJoinPoint joinPoint, Sm4Intercept annotation) {//获取接口的入参列表final Object[] params = joinPoint.getArgs();if (!CollectionUtil.isEmpty(params)) {//接口入参Object param = params[0];//接口入参对象的属性列表Field[] fields = param.getClass().getDeclaredFields();if (!CollectionUtil.isEmpty(annotation.paramFields())) {//遍历加密入参的属性值Arrays.stream(annotation.paramFields()).forEach(target -> {Field field = Arrays.stream(fields).filter(f -> f.getName().equals(target)).findFirst().orElse(null);if (null != field) {//反射获取目标属性值Object fieldValue = getFieldValue(param, field.getName());if (null != fieldValue) {String encryFieldValue = EncryptUtil.encryptEcb(key, fieldValue.toString());log.info("类{}的属性{}的值{}已被加密为{}", param.getClass().getName(), target, fieldValue, encryFieldValue);setFieldValue(param, field.getName(), encryFieldValue);}}});}}}/*** 解密响应结果对象的属性值** @param object* @param annotation*/private void decryptResponse(Object object, Sm4Intercept annotation) {//返回结果是list时if (object instanceof List) {decryptListObject((List) object, annotation);return;}//返回结果为单对象时decryptObject(object, annotation);}/*** 解密list中对象的属性值** @param list* @param annotation*/private void decryptListObject(List list, Sm4Intercept annotation) {list.stream().forEach(record -> decryptObject(record, annotation));}/*** 解密单对象的属性值** @param record* @param annotation*/private void decryptObject(Object record, Sm4Intercept annotation) {//接口返回对象的属性列表Field[] fields = record.getClass().getDeclaredFields();if (!CollectionUtil.isEmpty(annotation.respFields())) {//遍历加密入参的属性值Arrays.stream(annotation.respFields()).forEach(target -> {Field field = Arrays.stream(fields).filter(f -> f.getName().equals(target)).findFirst().orElse(null);if (null != field) {//反射获取目标属性值Object fieldValue = getFieldValue(record, field.getName());if (null != fieldValue) {String decryFieldValue = EncryptUtil.decryptEcb(key, fieldValue.toString());log.info("类{}的属性{}的值{}已被解密为{}", record.getClass().getName(), target, fieldValue, decryFieldValue);setFieldValue(record, field.getName(), decryFieldValue);}}});}}/*** 通过反射,用属性名称获得属性值** @param thisClass 需要获取属性值的类* @param fieldName 该类的属性名称* @return*/private Object getFieldValue(Object thisClass, String fieldName) {Object value = new Object();try {Method method = thisClass.getClass().getMethod(getMethodName(fieldName, "get"));value = method.invoke(thisClass);} catch (Exception e) {}return value;}/*** 通过反射,设置属性值** @param thisClass* @param fieldName* @param fieldValue*/private void setFieldValue(Object thisClass, String fieldName, Object fieldValue) {try {Method method = thisClass.getClass().getMethod(getMethodName(fieldName, "set"), String.class);method.invoke(thisClass, fieldValue);} catch (Exception e) {}}/*** 获取方法名称(getXXX,setXXX)** @param fieldName* @param methodPrefix* @return*/private String getMethodName(String fieldName, String methodPrefix) {return methodPrefix.concat(fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));}
}

第三步:mapper中定义注解@Encrypt进行数据拦截

@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {/*** 如果查询条件中包含username,则在mapper执行前进行加密* 如果返回数据中包含username及address等信息,则进行解密  * @param vo* @return*/@Encrypt(paramFields = {"userName"}, respFields = {"userName", "address"})List<UserInfo> findUserInfo(UserSearchVo vo);
}

这样,便实现了在数据查询(或更新、插入等)时,完成入参及返回数据的加解密操作。不过这种处理方式仅限于数据操作是通过Dao的mapper接口调用时,如果想处理更多场景,如通过mybatis-plus的Wraper方式进行数据处理时,则考虑用后面的第二种处理方式。

方案二:基于mybatis自带的扩展插件(plugins)实现

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

插件的使用参考实现如下(mybatis官方文档)

@Slf4j
@Component
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class
})})
public class ExamplePlugin implements Interceptor {private Properties properties = new Properties();public Object intercept(Invocation invocation) throws Throwable {// implement pre processing if needObject returnObject = invocation.proceed();// implement post processing if needreturn returnObject;}public void setProperties(Properties properties) {this.properties = properties;}
}

即在mybatis的Executor的query方法执行前后进行拦截,如事先定义好需要加解密的“配置规则”(如“对哪个表的哪些字段需要加解密”、“方法的出入参需加解密的字段”等等),然后拦截sql的请求参数及执行的返回结果,对其进行相应的数据加解密操作

本文的核心实现思路都是围绕spring aop进行实现的,足以说明aop思想的强大之处,大家平时学习工作中一定要勤学、多用、多练!希望本文可以帮助到有需要的朋友们!

数据安全之MySQL数据加解密的实现方案相关推荐

  1. Popular MVC框架请求响应数据加解密@Decrypt和@Encrypt的使用示例

    简介 此项目用于演示popularmvc如何提供统一全自动化的API隐私数据保护,并且可以做到业务无感和灵活指定数据加解密算法. 请求数据加密使用@Decrypt注解,响应信息加密使用@Encrypt ...

  2. STM32F1做RSA,AES数据加解密,MD5信息摘要处理

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_31878855/article/ ...

  3. 使用拦截器进行数据加解密

    文章目录 使用拦截器进行数据加解密 1. 加解密工具 3. 加解密字段注解 3.1 加密注解 3.2 解密注解 4. 封装加解密工具 5. 拦截器 6. 不同框架配置说明 6.1 springboot ...

  4. T-SQL问题解决集锦——数据加解密

    问题一:如何为数据进行加密与解密,避免使用者窃取机密数据? 对于一些敏感数据,如密码.卡号,一般不能使用正常数值来存储.否则会有安全隐患.以往的加密解密都有前端应用程序来辅助完成.而数据库一般只能加密 ...

  5. SpringBoot 优雅地对接口进行数据加解密

    我是 ABin-阿斌:写一生代码,创一世佳话,筑一览芳华.如果小伙伴们觉得不错就一键三连吧~ 声明: 原作者:掘金:https://juejin.cn/user/3650034336532824 原文 ...

  6. 护网必备技能:Spring Boot 接口数据加解密 功能实现

    护网必备技能:Spring Boot 接口数据加解密 功能实现 文章目录 护网必备技能:Spring Boot 接口数据加解密 功能实现 1. 尽量少改动,不影响之前的业务逻辑: 2. 考虑到时间紧迫 ...

  7. Spring Boot Post接口数据加解密

    概述 今天这篇文章聊一聊接口安全问题,涉及到接口的加密.解密. 接口数据加解密流程图 涉及客户端和服务端的整体改造,可以在接口统一加 /secret/ 前缀来区分. 代码Demo 用户类型: @Dat ...

  8. 数据加解密时Base64异常:Illegal base64 character 3f

    现象 用base64工具类对中文进行处理时出现异常,在数据加解密场景中经常使用 java.lang.IllegalArgumentException: Illegal base64 character ...

  9. 《ASP.NET Core 6框架揭秘》实例演示[19]:数据加解密与哈希

    数据保护(Data Protection)框架旨在解决数据在传输与持久化存储过程中的一致性(Integrity)和机密性(confidentiality)问题,前者用于检验接收到的数据是否经过篡改,后 ...

最新文章

  1. Double Precision Format(DPF)
  2. C语言数据类型大学霸IT达人
  3. 关于迭代器中IEnumerable与IEnumerator的区别
  4. 图论 —— DAG 图的最长路
  5. 实战!轻松搭建图像分类 AI 服务
  6. Java高级开发工程师面试考纲
  7. 深度剖析python_汉诺塔问题深度剖析(python实现)
  8. Tensorflow之计算tensor平均值
  9. Java语言实现查找两个字符串的最大公共字串
  10. Delphi开发人员指南 第一部份快速开发的基础 第2章 Object Pascal 语言(二)
  11. c语言最好的文本编辑器,【软件分享】两个小巧好用的C语言编辑器
  12. 宗海图cad_技术绘制论文,关于CAD技术机械制图关键技术相关参考文献资料-免费论文范文...
  13. aliez歌词_aLIEz FULL歌词【假名 罗马音】
  14. mysql下载安装驱动包 PowerDesigner连接
  15. fid fopen MATLAB
  16. matlab极坐标系作图,matlab极坐标作图
  17. 微信公众号文章采集方案
  18. Java程序员从笨鸟到菜鸟之(序言)+全部链接
  19. Python实现股票量化交易学习进阶(二)之简单交易策略的定义实现
  20. thinkphp用phpqrcode生成二维码(含中间带logo、临时二维码)或生成微信二维码海报的方法

热门文章

  1. php安全开发正则表达式,动态网页制作PHP常用的正则表达式
  2. 校园采花经历(超级爆笑)
  3. 金铜仙人辞汉歌-李贺
  4. 进阶篇:4)面向装配的设计DFA总章
  5. 饿了么官宣合作抖音后,美团的失意是什么?
  6. 概率论_证明_辛钦大数定律
  7. click是哪个键 wheel_Click是什么意思?键盘上的Click键在哪里?
  8. 语音计算机音乐学猫叫,语音控制开启家庭背景音乐系统新篇章
  9. Java实现PDF打印的解决方案
  10. webpack基本使用及配置