惊呆了!不改一行 Java 代码竟然就能轻松解决敏感信息加解密-阿里云开发者社区

开发者社区> 数据库> 正文
登录阅读全文

惊呆了!不改一行 Java 代码竟然就能轻松解决敏感信息加解密

简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 出于安全考虑,现需要将数据库的中敏感信息加密存储到数据库中,但是正常业务交互还是需要使用明文数据,所以查询返回我们还需要经过相应的解密才能返回给调用方。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


1

出于安全考虑,现需要将数据库的中敏感信息加密存储到数据库中,但是正常业务交互还是需要使用明文数据,所以查询返回我们还需要经过相应的解密才能返回给调用方。

ps:日常开发中,我们要有一定的安全意识,对于密码,金融数据等敏感信息事实加密存储保护。

这个需求说起来不是很难,我们只需要在执行 sql 之前,提前将指定数据进行加密。执行 sql 之后,获取返回结果,再进行的相应的解密。稍微改造下原有代码,很快完成需求。

2

现有加密算法如 RSA2 ,AES 等,密文长度将会是明文好几倍。上线加解密方案一定要评估数据库现有字段长度是否满足加密之后长度。

如果这是一张新建的表,上面的实现方案并没有什么问题。但是这次我们改造是几张已有已有千万级的存量的数据的表,这些数据都未被加密存储。

如果使用上述代码,使用加密之后的密文信息查询历史数据,当然查询不到任何结果。另外当查询返回的结果是明文,解密明文数据库也可能会导致相应的解密错误。

所以为了兼容历史数据,需要进行如下改造:

  • 增加新字段存放对应的加密数据,sql 等值条件查询修改成 in 查询
  • 查询返回的记录首先判断是否是密文,如果是密文再去解密

代码改造如下:

3

上述代码虽然解决业务需求,但是这个解决方案不是很优雅,业务代码改动较大,加解密的代码不能通用,所有涉及到相关字段的方法都需要改动,且几乎都是重复代码,代码侵入性很强,不是很友好。

有经验的同学可能会想到使用 Spring AOP 解决上述问题。

在切面的前置方法(beforeMethod)统一拦截查询参数,配合自定义的注解,加密指定的字段。

然后在切面的后置方法(afterReturn)拦截返回值,配合自定义注解,解密指定的字段。

Spring AOP 代码实现比较复杂,这里就不贴出具体的代码。

但是 Spring AOP 方案也并不通用,如果其他的应用也有相同的需求,同样的代码,又需要重复实现,还是很费时费力。

最终我们参考一个 github 开源项目『typehandlers-encrypt』,借助 mybatis 的 TypeHandler,实现通用的数据加解密解决方案。使用方只需要引入相关依赖,无需改动一行业务代码,仅需少量配置即可实现指定字段加解密操作,省时省力。

实现原理

mybatis 利用内置类型转换器(typeHandler),实现 Java 类型与 JDBC 类型的相互转换,我们正好可以利用这个特性,在转换之前加入加解密步骤。

typeHandler 底层原理不是复杂,如果我们没有使用 Mybatis,而是直接使用最原始的 JDBC 执行查询语句,相关代码如下:

4

我们需要手动判断 Java 类型,然后调用 PreparedStatement设置合适类型参数。获取返回结果之后,又需要手动调用 ResultSet 结果集获取相应类型的数据,这个过程十分繁琐。

使用 mybatis 之后,上述步骤就无需我们再实现了。mybatis 可以通过识别 Java/JDBC 类型,调用相应typeHandler,自动实现转换逻辑。

下图为 mybatis 内置类型转换器,基本涵盖了所有 Java/JDBC 数据类型。

通用解决方案

自定义 typeHandler

下面我们来实现带有加解密功能的类型转换器,实现方式也比较简单,只要继承 org.apache.ibatis.type.BaseTypeHandler,重写相关方法。

简单起见,上述加解密仅使用了 Base64,大家可以替换成相应加解密算法即或者引入相应加解密服务。

其中加密转换将在 setNonNullParameter 中执行,解密转换将在 getNullableResult中执行。

CryptTypeHandler 使用一个 MappedTypes 注解,包含一个 CryptType 类,这个类使用 mybatis 别名功能,可以极大简化 sqlmap 相关配置。

5

注册 typeHandler

使用方必须将 typeHandler 和 alias 注册到 mybatis 中,否则无法生效。

下面提供三种方式,可以根据项目情况选择其中一种即可:

单独使用 mybatis

这种场景需要在 mybatis-config.xml 配置,mybatis 启动时将会加载该配置文件。

1

使用 Spring 配置 Mybatis Bean

配合 Spring 使用时需要将 typeHandler 注入 SqlSessionFactoryBean ,配置方式如下:

2

SpringBoot

SpringBoot 方式就最简单了,只要引入 mybatis-starter,配置文件加入如下配置即可:

3

修改 mapper sql 配置

最后我们只要简单修改 mapper 中 resultMap 或 sql s配置就可以实现加解密。

假设我们对现有一张 bank_card 表进行加解密,表结构如下:

4

insert 加密

现需要对 card_no,phone,name,id_no 进行加密,insert 语句加密示例:

INSERT INTO bank_card (card_no, phone,name,id_no)
VALUES
(#{card_no,javaType=crypt},
#{phone,typeHandler=org.demo.type.CryptTypeHandler},
#{name,javaType=crypt},
#{id_no,javaType=crypt})

我们只需要在 #{} 指定 typeHandler,传入参数最后将被加密。使用 typeHandler需要使用类的全路径,比较繁琐,我们可以使用 javaType 属性,直接使用上面我们的定义别名 crypt。

数据库最终执行sql 如下:

INSERT INTO bank_card (card_no, phone,name,id_no) VALUES ('NjQzMjEyMzEyMzE=', 'MTM1Njc4OTEyMzQ=', '5rWL6K+V5Y2h', 'MTIzMTIzMTIzMQ==');

查询加解密

普通查询解密示例如下:

    <result property="card_no" column="card_no" typeHandler="org.demo.type.CryptTypeHandler"/>
    <result property="name" column="name" typeHandler="org.demo.type.CryptTypeHandler"/>
    <result property="id_no" column="id_no" typeHandler="org.demo.type.CryptTypeHandler"/>
    <result property="phone" column="phone" typeHandler="org.demo.type.CryptTypeHandler"/>


    select * from bank_card where id=#{id}

这里我们在 select 配置中只能使用 resultMap 属性,指定 typeHandler 。

数据库明文、密文共存的情况,查询解密示例如下:

5

最后我们可以将自定义的 typeHandler 单独打包发布,其他业务方只需要引用,改造相关配置文件,即可完成数据加解密。

总结

借助于自定义的 typeHandler,我们实现了一个通用的加解密的方案,该方案对于使用方来说代码侵入性小,开箱即用,可以快速完成加解密的改造。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-04-09
本文作者:楼下小黑哥
本文来自:“掘金”,了解相关信息可以关注“掘金”

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
数据库
使用钉钉扫一扫加入圈子
+ 订阅

分享数据库前沿,解构实战干货,推动数据库技术变革

其他文章