需求
不想服务器运维人员或管理员对服务器上的文件进行直接访问,可以得到内容。
不想数据库运维人员或管理员对数据库表中的内容直接查询得到。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| public class ByteArrayUtils {
public static boolean contains(byte[] a, byte[] b) { if (a == null || b == null || a.length < b.length) { return false; } for (int i = 0; i <= a.length - b.length; i++) { int j; for (j = 0; j < b.length; j++) { if (a[i + j] != b[j]) { break; } } if (j == b.length) { return true; } } return false; }
public static boolean startsWith(byte[] a, byte[] b) { if (a == null || b == null || a.length < b.length) { return false; }
for (int i = 0; i < b.length; i++) { if (a[i] != b[i]) { return false; } }
return true; }
public static byte[] prepend(byte[] b, byte[] a) { if (b == null) { return a; } if (a == null) { return b; }
byte[] result = new byte[b.length + a.length];
System.arraycopy(b, 0, result, 0, b.length);
System.arraycopy(a, 0, result, b.length, a.length);
return result; }
public static byte[] removeStart(byte[] a, byte[] b) {
byte[] result = new byte[a.length - b.length];
System.arraycopy(a, b.length, result, 0, result.length);
return result; }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| public class AESUtilWithPrefix { private static AES getEncryptor(String secretKeyStr) { AES aes = SecureUtil.aes(secretKeyStr.getBytes(StandardCharsets.UTF_8)); return aes; }
public static String decrypt(String value, String prefix, String secretKeyStr) { if (value == null) { return null; } if (!StringUtils.hasText(value)) { return ""; } if (StringUtils.hasText(prefix)) { if (!value.startsWith(prefix)) { return value; } else { value = value.substring(prefix.length()); } } return getEncryptor(secretKeyStr).decryptStr(value); }
public static String encrypt(String rawValue, String prefix, String secretKeyStr) { if (rawValue == null) { return null; } if (!StringUtils.hasText(rawValue)) { return ""; } String encryptStr = getEncryptor(secretKeyStr).encryptBase64(rawValue); return prefix + encryptStr; }
public static byte[] decrypt(byte[] value, String prefix, String secretKeyStr) { if (value == null) { return null; } if (value.length <= 0) { return new byte[0]; } byte[] needDecrypt = value; if (StringUtils.hasText(prefix)) { if (!ByteArrayUtils.startsWith(value, prefix.getBytes(StandardCharsets.UTF_8))) { return value; } else { needDecrypt = ByteArrayUtils.removeStart(value, prefix.getBytes(StandardCharsets.UTF_8)); } } return getEncryptor(secretKeyStr).decrypt(needDecrypt); }
public static byte[] encrypt(byte[] rawValue, String prefix, String secretKeyStr) { if (rawValue == null) { return new byte[0]; } if (rawValue.length <= 0) { return new byte[0]; } byte[] encrypt = getEncryptor(secretKeyStr).encrypt(rawValue); return ByteArrayUtils.prepend(prefix.getBytes(StandardCharsets.UTF_8), encrypt); }
public static void main(String[] args) { String str = "helloword"; str = encrypt(str, "entrypted_", "1234567890123456"); System.out.println(str); str = decrypt(str, "entrypted_", "1234567890123456"); System.out.println(str); }
}
|
此工具类兼容不加密情况。
文件加解密
使用工具类中byte数组的加解密,对文件内容加解密,存储本地文件时加密,httpresponse响应文件时解密
数据库字段加解密
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DecryptEncryptClass {
}
|
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface DecryptEncryptField {
}
|
查询解密方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| @Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) }) @Slf4j public class DecryptInterceptor implements Interceptor {
@Value("${aes.enable:false}") private Boolean aesEnable;
@Value("${aes.prefix:'entrypted_'}") private String aesPrefix;
@Value("${aes.secretKeyStr:'1234567890123456'}") private String aesSecretKeyStr;
@Override public Object intercept(Invocation invocation) throws Throwable { Object result = invocation.proceed(); if (result instanceof List) { List<?> lists = (List<?>) result; if (!lists.isEmpty()) { if (Objects.nonNull(lists.get(0)) && lists.get(0).getClass().isAnnotationPresent(DecryptEncryptClass.class)) { handlerResult(lists); } } } return result; }
private void handlerResult(List<?> lists) throws Exception { try { Map<String, Field> decMaps = new HashMap<>(); Class<?> clasz = lists.get(0).getClass(); Field[] fields = clasz.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(DecryptEncryptField.class)) { decMaps.put(field.getName(), field); } } for (Object obj : lists) { if (Objects.isNull(obj)) { continue; } for (Map.Entry<String, Field> entity : decMaps.entrySet()) { Field sourceField = entity.getValue(); handlerField(obj, sourceField); } }
} catch (Exception e) { log.error("error", e); throw e; } }
private void handlerField(Object obj, Field sourceField) throws IllegalArgumentException, IllegalAccessException { sourceField.setAccessible(true); String sourceStr = (String) sourceField.get(obj); if (StringUtils.isEmpty(sourceStr)) { return; } else { String str = AESUtilWithPrefix.decrypt(sourceStr, aesPrefix, aesSecretKeyStr); sourceField.set(obj, str); }
}
@Override public Object plugin(Object target) { return Plugin.wrap(target, this); }
@Override public void setProperties(Properties properties) {
}
}
|
修改方法加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| @Intercepts( { @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) } ) public class EncryptInterceptor implements Interceptor {
@Value("${aes.enable:false}") private Boolean aesEnable;
@Value("${aes.prefix:'entrypted_'}") private String aesPrefix;
@Value("${aes.secretKeyStr:'1234567890123456'}") private String aesSecretKeyStr;
@Override public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs(); Object parameter = args[1];
if (parameter instanceof Map<?, ?>) { Map<?, ?> params = (Map<?, ?>) parameter; for (Object param : params.values()) { handleParameter(param); } } else { handleParameter(parameter); }
return invocation.proceed(); }
private void handleParameter(Object parameter) throws IllegalArgumentException, IllegalAccessException { if (parameter != null && parameter.getClass().isAnnotationPresent(DecryptEncryptClass.class)) { Field[] fields = parameter.getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(DecryptEncryptField.class)) { field.setAccessible(true); String sourceVal = (String) field.get(parameter); if (sourceVal == null) { continue; } if (StringUtils.isEmpty(sourceVal)) { continue; } if (sourceVal.startsWith(aesPrefix)) { continue; } String encStr = AESUtilWithPrefix.encrypt(sourceVal, aesPrefix, aesSecretKeyStr); field.setAccessible(true); field.set(parameter, encStr); } } }
}
@Override public Object plugin(Object target) { return Plugin.wrap(target, this); }
@Override public void setProperties(Properties properties) {
}
}
|
交给spring
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration public class MybatisConfig { @Bean public DecryptInterceptor decryptInterceptor() { return new DecryptInterceptor(); }
@Bean public EncryptInterceptor entryptInterceptor() { return new EncryptInterceptor(); }
}
|
这里交给spring, 让spring 可以注入参数及 注入进mybatis的InterceptorChain中。
调试
看是否可注入进InterceptorChain一次,是否正确加解密。