关于数据加密的思考,加密的算法类型,优雅解决数据加密

背景: 海外项目,考虑到数据合规的问题,项目中入库的数据进行加密,但正常的业务交集还是要用明文数据,所以查询出来的数据要求明文返给前端。

传统的做法: 在数据入库前进行数据加密,把数据查询出来前,再进行数据对应的加密,这是最简单的实现,但会有比较多的侵入式编程。使用 AOP 的话代码会比较复杂,也不算很通用。

最佳解决方案: 是用借助 mybatis 的 「TypeHandler」,仅需引入对应的依赖,少量的配置,少量的改动既能实现数据加密的方案。

实现步骤:

1、自定义 typeHandler 继承 org.apache.ibatis.type.BaseTypeHandler ,重写相关方法

/**
 * @author lwc
 */
@Slf4j
@MappedTypes(Encrypt.class)
public class CryptTypeHandler extends BaseTypeHandler<String> {


    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        try {
            String encryptStr = Base64Utils.encodeToString(s.getBytes(Charset.defaultCharset()));
            preparedStatement.setString(i, encryptStr);
        } catch (Exception e) {
            log.error("数据加密失败", e);
            // 如果加密失败使用,则保证入库原始数据
            preparedStatement.setString(i, s);
        }
    }

    @Override
    public String getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        return decrypt(resultSet.getString(columnName));
    }

    @Override
    public String getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        return decrypt(resultSet.getString(columnIndex));
    }

    @Override
    public String getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        return decrypt(callableStatement.getString(columnIndex));
    }

    /**
     * 解密相关的参数,如果解密失败
     * @param param
     * @return
     */
    private String decrypt(String param) {
        // 判断返回结果是否是密文,不是密文那不进行解密
        if (!isEncrypt(param)) {
            return param;
        }
        try {
            byte[] bytes = Base64Utils.decodeFromString(param);
            String result = new String(bytes, Charset.defaultCharset());
            return result;
        } catch (Exception e) {
            log.error("数据加密失败", e);
            return param;
        }
    }

    /**
     * 判断字符串是否是密文
     * @param param
     * @return
     */
    private boolean isEncrypt(String param) {
        // 使用正则判断,是否是Base64密文
        String base64Pattern = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$";
        return Pattern.matches(base64Pattern, param);
    }
}

说明:
加密转换在 setNonNullParameter 中执行,解密在 getNullableResult中执行。

2、 CryptTypeHandler 使用一个 MappedTypes 注解,包含一个 CryptType 类,这个类使用 mybatis
别名功能,可以极大简化 mapper sql 的编写。

@Alias("encrypt") 	public class Encrypt {}

3、注册 typeHandler , SpringBoot 项目配置方式

# 类型转换器包路径
mybatis.type-handlers-package=com.xx.xx.x
mybatis.type-aliases-package=com.xx.xx

4、mapper.xml 文件改造

//新增数据 ,只需在 #{} 指定typeHandler , 传入参数最后被加密, 配置typeHandler需要全路径,或者使用较简单的javaType属性,使用上面定义的 encrypt 
<insert id="insertBankCard" keyProperty="id" useGeneratedKeys="true" parameterType="com.gwm.prism.bank">
    INSERT INTO bank (card_number, phone, user_name, id_number)
    VALUES
    (#{cardNumber,javaType=encrypt},
    #{phone,typeHandler=com.gwm.prism.handler.CryptTypeHandler},
    #{userName,typeHandler=com.gwm.prism.handler.CryptTypeHandler},
    #{idNumber,javaType=encrypt})
</insert>

//查询在resultMap属性,指定 typeHandler
<resultMap id="bankResultMap" type="org.demo.pojo.BankCardDO">
        <result property="cardNumber" column="card_number" typeHandler="com.gwm.prism.handler.CryptTypeHandler"/>
        <result property="userName" column="user_name" typeHandler="com.gwm.prism.handler.CryptTypeHandler"/>
        <result property="idNumber" column="id_number" typeHandler="com.gwm.prism.handler.CryptTypeHandler"/>
        <result property="phone" column="phone" typeHandler="com.gwm.prism.handler.CryptTypeHandler"/>
</resultMap>
<select id="queryBank" resultMap="bankResultMap">
        select * from bank;
</select>
常用加密算法的类型:

1、 非对称加密

  • 密钥:加解密钥相同
  • 缺点:无法确保密钥被安全传递
  • 常用算法:DES、3DES(TripleDES)、AES等

2、对称加密

  • 密钥:公私钥加密对,公钥加密,私钥解密
  • 公钥由私钥生成,私钥可以推导出公钥,但是从公钥无法推出私钥
  • 优点:解决了密钥传输中的安全问题
  • 常用算法:RSA、ECC(椭圆曲线加密算法)
  • 使用场景: SSH安全验证等
  • 缺点:解决了信息传送的问题,但是又引入了新问题,即无法验证发送方是正确的,就是说,可能被伪造成发送方

3、哈希加密

  • 将一段数据(任意长度)经过计算装换成一段定长的数据
    不可逆性:几乎无法通过哈希的结果推导出原文
  • 无碰撞性:两个不同原文哈希后的结果一定不同
  • 常用算法:MD5,SHA256
  • 使用场景:
    数据库中的用户密码存储(MD5)
    挖矿计算(SHA256)

4、数字签名加密

  • 密钥:私钥签名,公钥验证签名
  • 使用场景: 比特币交易验证等

Java 中常用的加密类型参考:https://www.jianshu.com/p/c83810a3479f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不像程序猿的程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值