diff --git a/kernel-d-security/pom.xml b/kernel-d-security/pom.xml
index 8e5942396..2b58463a9 100644
--- a/kernel-d-security/pom.xml
+++ b/kernel-d-security/pom.xml
@@ -23,6 +23,7 @@
+ * 可根据自身需要自定义实现,默认实现为AES + * + * @author majianguo + * @date 2021/7/3 11:02 + */ +public interface EncryptAlgorithmApi { + + /** + * 加密算法 + * + * @param encryptedData 加密数据 + * @return {@link java.lang.String} + * @author majianguo + * @date 2021/7/3 11:07 + **/ + String encrypt(String encryptedData); + + /** + * 解密算法 + * + * @param cipher 待解密密文 + * @return {@link java.lang.String} + * @author majianguo + * @date 2021/7/3 11:33 + **/ + String decrypt(String cipher); +} diff --git a/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/algorithm/impl/AesEncryptAlgorithmApiImpl.java b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/algorithm/impl/AesEncryptAlgorithmApiImpl.java new file mode 100644 index 000000000..ea034818f --- /dev/null +++ b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/algorithm/impl/AesEncryptAlgorithmApiImpl.java @@ -0,0 +1,34 @@ +package cn.stylefeng.roses.kernel.security.database.algorithm.impl; + +import cn.hutool.crypto.symmetric.SymmetricAlgorithm; +import cn.hutool.crypto.symmetric.SymmetricCrypto; +import cn.stylefeng.roses.kernel.security.database.algorithm.EncryptAlgorithmApi; + +/** + * AES 加密解密实现 + * + * @author majianguo + * @date 2021/7/3 11:43 + */ +public class AesEncryptAlgorithmApiImpl implements EncryptAlgorithmApi { + + /** + * AES加密实体类 + */ + public final SymmetricCrypto symmetricCrypto; + + public AesEncryptAlgorithmApiImpl(byte[] key) { + symmetricCrypto = new SymmetricCrypto(SymmetricAlgorithm.AES, key); + } + + @Override + public String encrypt(String encryptedData) { + return symmetricCrypto.encryptHex(encryptedData); + } + + @Override + public String decrypt(String cipher) { + return symmetricCrypto.decryptStr(cipher); + } + +} diff --git a/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/EncryptField.java b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/EncryptField.java new file mode 100644 index 000000000..f4a9fd306 --- /dev/null +++ b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/EncryptField.java @@ -0,0 +1,19 @@ +package cn.stylefeng.roses.kernel.security.database.annotation; + +import java.lang.annotation.*; + +/** + * 需要加密字段注解 + *
+ * 该注解作用范围在字段上面(该类需要加 {@link ProtectedData} 注解) + * + * @author majianguo + * @date 2021/7/3 10:57 + */ +@Documented +@Inherited +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EncryptField { + +} diff --git a/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/ProtectedData.java b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/ProtectedData.java new file mode 100644 index 000000000..fc018fbc3 --- /dev/null +++ b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/ProtectedData.java @@ -0,0 +1,18 @@ +package cn.stylefeng.roses.kernel.security.database.annotation; + +import java.lang.annotation.*; + +/** + * 被保护数据标识注解(标识那个DTO需要加解密) + *
+ * 该注解作用范围在类上面 + * + * @author majianguo + * @date 2021/7/3 10:54 + */ +@Inherited +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProtectedData { + +} diff --git a/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/ProtectedField.java b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/ProtectedField.java new file mode 100644 index 000000000..0e00c2401 --- /dev/null +++ b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/annotation/ProtectedField.java @@ -0,0 +1,19 @@ +package cn.stylefeng.roses.kernel.security.database.annotation; + +import java.lang.annotation.*; + +/** + * 需要加解密字段注解(该字段自动加解密,数据库是密文,查看时是明文) + *
+ * 该注解作用范围在字段上面(该类需要加 {@link ProtectedData} 注解)
+ *
+ * @author majianguo
+ * @date 2021/7/5 9:18
+ */
+@Documented
+@Inherited
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ProtectedField {
+
+}
diff --git a/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/interceptor/ParameterInterceptor.java b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/interceptor/ParameterInterceptor.java
new file mode 100644
index 000000000..3e7b9d36c
--- /dev/null
+++ b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/interceptor/ParameterInterceptor.java
@@ -0,0 +1,90 @@
+package cn.stylefeng.roses.kernel.security.database.interceptor;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.stylefeng.roses.kernel.security.database.algorithm.EncryptAlgorithmApi;
+import cn.stylefeng.roses.kernel.security.database.annotation.EncryptField;
+import cn.stylefeng.roses.kernel.security.database.annotation.ProtectedData;
+import cn.stylefeng.roses.kernel.security.database.annotation.ProtectedField;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.plugin.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Field;
+import java.sql.PreparedStatement;
+import java.util.Properties;
+
+/**
+ * Mybatis拦截器,拦截入库参数
+ *
+ * @author majianguo
+ * @date 2021/7/3 11:58
+ */
+@Slf4j
+@Component
+@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),})
+public class ParameterInterceptor implements Interceptor {
+
+ @Autowired
+ private EncryptAlgorithmApi encryptAlgorithmApi;
+
+ @Override
+ public Object intercept(Invocation invocation) throws Throwable {
+
+ // 若指定ResultSetHandler ,这里则能强转为ResultSetHandler
+ ParameterHandler parameterHandler = (ParameterHandler)invocation.getTarget();
+
+ // 获取参数对像,即 mapper 中 paramsType 的实例
+ Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
+ parameterField.setAccessible(true);
+
+ // 取出实例
+ Object parameterObject = parameterField.get(parameterHandler);
+ if (parameterObject != null) {
+ Class> parameterObjectClass = parameterObject.getClass();
+
+ // 校验该实例的类是否被@ProtectedData所注解
+ ProtectedData protectedData = AnnotationUtils.findAnnotation(parameterObjectClass, ProtectedData.class);
+ if (ObjectUtil.isNotNull(protectedData)) {
+
+ //取出当前当前类所有字段
+ Field[] declaredFields = parameterObjectClass.getDeclaredFields();
+
+ // 处理需要加密的字段
+ for (Field declaredField : declaredFields) {
+
+ // 包含其中任意一个即可
+ ProtectedField protectedField = declaredField.getAnnotation(ProtectedField.class);
+ EncryptField encryptField = declaredField.getAnnotation(EncryptField.class);
+ if (ObjectUtil.isNotNull(protectedField) || ObjectUtil.isNotNull(encryptField)) {
+ declaredField.setAccessible(true);
+ Object fieldData = declaredField.get(parameterObject);
+ // 如果是String就处理
+ if (fieldData instanceof String) {
+ String value = (String)fieldData;
+ try {
+ String encrypt = encryptAlgorithmApi.encrypt(value);
+ declaredField.set(parameterObject, encrypt);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ }
+ return invocation.proceed();
+ }
+
+ @Override
+ public Object plugin(Object target) {
+ return Plugin.wrap(target, this);
+ }
+
+ @Override
+ public void setProperties(Properties properties) {
+
+ }
+}
diff --git a/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/interceptor/ResultInterceptor.java b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/interceptor/ResultInterceptor.java
new file mode 100644
index 000000000..661e9fd35
--- /dev/null
+++ b/kernel-d-security/security-sdk-database-field/src/main/java/cn/stylefeng/roses/kernel/security/database/interceptor/ResultInterceptor.java
@@ -0,0 +1,135 @@
+package cn.stylefeng.roses.kernel.security.database.interceptor;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.stylefeng.roses.kernel.security.database.algorithm.EncryptAlgorithmApi;
+import cn.stylefeng.roses.kernel.security.database.annotation.ProtectedData;
+import cn.stylefeng.roses.kernel.security.database.annotation.ProtectedField;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.executor.resultset.ResultSetHandler;
+import org.apache.ibatis.plugin.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Field;
+import java.sql.Statement;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+
+/**
+ * Mybatis拦截器,拦截返回参数
+ *
+ * @author majianguo
+ * @date 2021/7/3 11:58
+ */
+@Slf4j
+@Component
+@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
+public class ResultInterceptor implements Interceptor {
+
+ @Autowired
+ private EncryptAlgorithmApi encryptAlgorithmApi;
+
+ @Override
+ public Object intercept(Invocation invocation) throws Throwable {
+
+ //取出查询的结果
+ Object resultObject = invocation.proceed();
+ if (Objects.isNull(resultObject)) {
+ return null;
+ }
+
+ // 判断结果是List还是对象
+ if (resultObject instanceof List) {
+ List resultList = (List)resultObject;
+ // 判断是否为空
+ if (ObjectUtil.isNotNull(resultList)) {
+ // 处理数据
+ for (Object result : resultList) {
+ // 对象处理
+ this.objectProcessing(result);
+ }
+ }
+ } else {
+ // 处理单个对象
+ this.objectProcessing(resultObject);
+ }
+
+ return resultObject;
+ }
+
+ /**
+ * 对象处理
+ *
+ * @return
+ * @author majianguo
+ * @date 2021/7/5 9:52
+ **/
+ private void objectProcessing(Object result) throws IllegalAccessException {
+ Class> resultClass = result.getClass();
+ // 是否加注解了
+ ProtectedData annotation = AnnotationUtils.findAnnotation(resultClass, ProtectedData.class);
+
+ // 加注解就去处理
+ if (ObjectUtil.isNotNull(annotation)) {
+ Field[] declaredFields = resultClass.getDeclaredFields();
+ for (Field field : declaredFields) {
+ // 处理字段
+ this.fieldProcessing(result, field);
+ }
+ }
+ }
+
+ /**
+ * @param result
+ * @param field
+ * @return
+ * @author majianguo
+ * @date 2021/7/5 9:52
+ **/
+ private void fieldProcessing(Object result, Field field) throws IllegalAccessException {
+ if (this.isTag(field)) {
+ field.setAccessible(true);
+ Object object = field.get(result);
+ //String的解密
+ if (object instanceof String) {
+ String value = (String)object;
+ //对注解的字段进行逐一解密
+ try {
+ String decrypt = encryptAlgorithmApi.decrypt(value);
+ field.set(result, decrypt);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * 是否注解标记了
+ *
+ * @param field 被判断字段
+ * @return {@link boolean}
+ * @author majianguo
+ * @date 2021/7/5 9:35
+ **/
+ private boolean isTag(Field field) {
+ // 包含其中任意一个即可
+ ProtectedField protectedField = field.getAnnotation(ProtectedField.class);
+ if (ObjectUtil.isNotNull(protectedField)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Object plugin(Object target) {
+ return Plugin.wrap(target, this);
+ }
+
+ @Override
+ public void setProperties(Properties properties) {
+
+ }
+}
diff --git a/kernel-d-security/security-spring-boot-starter/pom.xml b/kernel-d-security/security-spring-boot-starter/pom.xml
index 02a88e64c..f8a469d5a 100644
--- a/kernel-d-security/security-spring-boot-starter/pom.xml
+++ b/kernel-d-security/security-spring-boot-starter/pom.xml
@@ -52,6 +52,13 @@