积分商城用户数据加密改造实战技术博客
- 实现策略: 使用 AES 加密算法,将敏感字段加密后存储,同时保留原始数据处理方式以实现同时兼容。
- 解决方案: 通过 MyBatis TypeHandler 解耦存取过程,自动将某些指定字段进行加密和解密操作,无需修改上应应用逻辑。
- 分步执行:
- 分析需加密字段
- 编写AES加密/解密公共类
- 实现MyBatis TypeHandler
- 数据移植工具脚本,实现旧数据加密
3. 具体技术进程
3.1 分析字段
根据数据存储结构,确定哪些字段需要加密:
- 用户姓名
- 电话号码
- 邮箱
- 身份证号
3.2 AES加密工具
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AesUtil {
private static final String ALGORITHM = "AES";
private static final String KEY = "1234567890abcdef"; // 16位密钥
public static String encrypt(String value) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String encrypted) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decoded = Base64.getDecoder().decode(encrypted);
byte[] original = cipher.doFinal(decoded);
return new String(original);
}
}
3.3 实现MyBatis TypeHandler
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class EncryptTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
try {
ps.setString(i, AesUtil.encrypt(parameter));
} catch (Exception e) {
throw new SQLException("Encryption failed", e);
}
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
try {
return AesUtil.decrypt(rs.getString(columnName));
} catch (Exception e) {
return null;
}
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
try {
return AesUtil.decrypt(rs.getString(columnIndex));
} catch (Exception e) {
return null;
}
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
return AesUtil.decrypt(cs.getString(columnIndex));
} catch (Exception e) {
return null;
}
}
}
配合 MyBatis Mapper 使用:
<result property="phone" column="phone" typeHandler="xxx.EncryptTypeHandler" />
3.4 数据移植脚本
写一个移植脚本,将旧数据转为加密形式。
基本步骤:
- 找出所有旧数据
- 调用AesUtil进行加密
- 更新回原数据表
示例:
List<User> users = userDao.findAll();
for (User user : users) {
String encryptedPhone = AesUtil.encrypt(user.getPhone());
userDao.updatePhone(user.getId(), encryptedPhone);
}
4. 实践结果
完成数据加密改造后,系统正常运行,上线用户体验无明显变化,同时提升了数据安全性,成功满足后续安全管控需求。
整体过程中,最关键的是定制好规范,遵循加密协议,确保各组件互相兼容。实际工程中,还可考虑增加片段加密(如通过指定段名来分类加密),更精精刻控制实施效果。