• 需求背景

众所周知,金融行业有各种各样的财务报表,有些报表涉及到公司财务或经营相关的敏感数据,需要进行加密存储,只有掌握密钥的用户才能看到解密后的数据。注意,这里所说的加密并不是针对整个数据库或者表全局加密,而是针对表的某些字段进行加密。

  • 实现思路

1、针对某一张报表创建相对应的一张落地表,相关需要加密的字段统一定义为VARCHAR2(1000)。

2、实现Hibernate监听器接口,在实体保存之前进行加密,数据Load出来之后进行解密,这样可以实现加密解密逻辑的统一处理。

3、对是否需要加密的实体和字段标注对应的注解,将加密解密的实现逻辑转化为对注解的解析处理。

4、统一采用AES加密解密算法,放在工具类中实现,加密解密流程(原图摘自:https://blog.csdn.net/gulang03/article/details/81175854)如下:

5、其他需要处理的场景

a、数据的保存和查询采用的是JDBC的处理方式,需要在SQL中统一处理(建议用JdbcTemplate底层统一封装下),mysql有内置函数,oracle需要自定义函数实现

b、数据修改时也需要考虑加密的场景

c、oracle正常情况,用户不能访问sys.dbms_crypto,需要DBA授权

  •  核心代码

1、注册监听器事件

public class HibernateEvent {

    @Autowired
private SessionFactory sessionFactory;
@Autowired
private AesListener aesListener; @PostConstruct
public void registerListeners() {
EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(aesListener);
registry.getEventListenerGroup(EventType.POST_LOAD).appendListener(aesListener);
} }

2、监听器实现

@Component
public class AesListener implements PreInsertEventListener, PostLoadEventListener {
@Override
public boolean onPreInsert(PreInsertEvent event) { Class clazz = event.getPersister().getMappedClass(); String[] propertyNames = event.getPersister().getPropertyNames(); EnableEncrypted enableEncrypted = (EnableEncrypted)clazz.getAnnotation(EnableEncrypted.class); if(enableEncrypted != null){
Field[] fields = clazz.getDeclaredFields();
if(fields != null){
for(Field field : fields){
boolean isAccessible = field.isAccessible();
if(!isAccessible){
field.setAccessible(true);
}
Encrypted encrypted = field.getAnnotation(Encrypted.class);
if(encrypted != null){
try {
Object value = field.get(event.getEntity()); Class<?> type = field.getType();
String enStr = null;
if(type == String.class){
enStr = AesUtils.aesEncrypt(value.toString());
}else if(type == Double.class){
Double val = (Double)value;
enStr = AesUtils.aesDecrypt(String.valueOf(val));
} if(enStr != null){
field.set(event.getEntity(), enStr);
field.setAccessible(isAccessible); for(int i = 0; i < propertyNames.length; i++){
if(field.getName().equals(propertyNames[i])){
event.getState()[i] = enStr;
break;
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
return false;
}
}

3、加密解密工具类

public class AesUtils {

    public static String selectSql(String columnName){
if(StringUtils.isNotEmpty(columnName)){
String alias = columnName.contains(".")?columnName.substring(columnName.lastIndexOf(".")+1):columnName;
return whereSql(columnName) + " AS "+alias;
}
return null;
} public static String whereSql(String columnName){
if(StringUtils.isNotEmpty(columnName)){
return "CAST(AES_DECRYPT(UNHEX("+columnName+"), '"+ AesConstants.AES_KEY +"') AS CHAR)";
}
return null;
} public static String writeSql(String columnValue){
if(StringUtils.isNotEmpty(columnValue)){
return "HEX(AES_ENCRYPT('"+columnValue+"', '"+AesConstants.AES_KEY+"'))";
}
return null;
} private static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
} private static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
} public static String aesEncrypt(String content) {
try {
SecretKey key = generateMySQLAESKey("ASCII");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cleartext = content.getBytes("UTF-8");
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(Hex.encodeHex(ciphertextBytes)); } catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
} public static String aesDecrypt(String content){
try {
SecretKey key = generateMySQLAESKey("ASCII");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] cleartext = new byte[0];
try {
cleartext = Hex.decodeHex(content.toCharArray());
} catch (DecoderException e) {
log.error("非法的16进制字符串:" + content, e);
}
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(ciphertextBytes, "UTF-8"); } catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
} public static SecretKeySpec generateMySQLAESKey(final String encoding) {
try {
final byte[] finalKey = new byte[16];
int i = 0;
for(byte b : AesConstants.AES_KEY.getBytes(encoding))
finalKey[i++%16] ^= b;
return new SecretKeySpec(finalKey, "AES");
} catch(UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
} }

最新文章

  1. Windows Phone 二十一、联系人存储
  2. 【hadoop】——压缩工具比较
  3. js倒计时(可定义截止日期)
  4. 关于git提交的自己的理解
  5. ytu 1910:字符统计(水题)
  6. 【原创】一个基于简单剪枝的DFS解数独程序
  7. 常用几种Java Web容器
  8. co源码解析
  9. mysql中OPTIMIZE TABLE的作用
  10. jsp EL表达式原样输出问题
  11. 微服务架构中APIGateway原理
  12. SQL Server - 约束 CONSTRAINT
  13. Problem C: 文体双花 解题报告
  14. intellij idea svn 修改文件后,父文件夹也标注修改
  15. Camera插件推荐,解锁电影大师级视角控制
  16. [javaSE] 网络编程(TCP,UDP,Socket特点)
  17. linux服务器登录时慢出现卡顿
  18. 1 - bootstrap基本模板
  19. [批处理]批量提取MKV资源
  20. bzoj1040(ZJOI2008)骑士——基环树

热门文章

  1. SpringBoot:整合Shiro
  2. Java的Object.wait(long)在等待时间过去后会继续往后执行吗
  3. static RMQ
  4. java项目部署Linux服务器几种启动方式总结经验
  5. 重要的serialVersionUID
  6. iNeuOS工业互联平台,实现动态图元、计算平台、远程控制、数据转发等,和大厂相比如何
  7. RobotFramework自动化测试之元素定位
  8. vue中事件代理
  9. python3.x 基础三:set集合
  10. java循环map