mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-10 20:57:18 +08:00
feat(security/crypto): 新增安全模块-加密,支持 MyBatis ORM 框架字段加密
This commit is contained in:
@@ -52,12 +52,17 @@ public class PropertiesConstants {
|
|||||||
/**
|
/**
|
||||||
* 安全配置
|
* 安全配置
|
||||||
*/
|
*/
|
||||||
public static final String SECURITY = CONTINEW_STARTER + ".security";
|
public static final String SECURITY = CONTINEW_STARTER + StringConstants.DOT + "security";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 密码编解码配置
|
* 密码编解码配置
|
||||||
*/
|
*/
|
||||||
public static final String PASSWORD = SECURITY + ".password";
|
public static final String PASSWORD = SECURITY + StringConstants.DOT + "password";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加/解密配置
|
||||||
|
*/
|
||||||
|
public static final String CRYPTO = SECURITY + StringConstants.DOT + "crypto";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web 配置
|
* Web 配置
|
||||||
|
@@ -120,6 +120,11 @@
|
|||||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
<version>${mybatis-plus.version}</version>
|
<version>${mybatis-plus.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-core</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Dynamic Datasource(基于 Spring Boot 的快速集成多数据源的启动器) -->
|
<!-- Dynamic Datasource(基于 Spring Boot 的快速集成多数据源的启动器) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -382,6 +387,13 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 安全模块 - 加密 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-security-crypto</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 安全模块 - 脱敏 -->
|
<!-- 安全模块 - 脱敏 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.charles7c.continew</groupId>
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-security</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-security-crypto</artifactId>
|
||||||
|
<description>ContiNew Starter 安全模块 - 加密</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Hutool 加密解密模块(封装 JDK 中加密解密算法) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis Plus(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.annotation;
|
||||||
|
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.enums.Algorithm;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段加/解密注解
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface FieldEncrypt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密/解密算法
|
||||||
|
*/
|
||||||
|
Algorithm value() default Algorithm.AES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密/解密处理器
|
||||||
|
* <p>
|
||||||
|
* 优先级高于加密/解密算法
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
Class<? extends IEncryptor> encryptor() default IEncryptor.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称加密算法密钥
|
||||||
|
*/
|
||||||
|
String password() default "";
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.autoconfigure;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.core.MyBatisDecryptInterceptor;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.core.MyBatisEncryptInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加/解密自动配置
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@AutoConfiguration
|
||||||
|
@EnableConfigurationProperties(CryptoProperties.class)
|
||||||
|
@ConditionalOnProperty(prefix = PropertiesConstants.CRYPTO, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||||
|
public class CryptoAutoConfiguration {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CryptoAutoConfiguration.class);
|
||||||
|
private final CryptoProperties properties;
|
||||||
|
|
||||||
|
public CryptoAutoConfiguration(CryptoProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis 加密拦截器配置
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(MyBatisEncryptInterceptor.class)
|
||||||
|
public MyBatisEncryptInterceptor myBatisEncryptInterceptor() {
|
||||||
|
return new MyBatisEncryptInterceptor(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis 解密拦截器配置
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(MyBatisDecryptInterceptor.class)
|
||||||
|
public MyBatisDecryptInterceptor myBatisDecryptInterceptor() {
|
||||||
|
return new MyBatisDecryptInterceptor(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postConstruct() {
|
||||||
|
log.debug("[ContiNew Starter] - Auto Configuration 'Security-Crypto' completed initialization.");
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.autoconfigure;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加/解密配置属性
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(PropertiesConstants.CRYPTO)
|
||||||
|
public class CryptoProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用加/解密配置
|
||||||
|
*/
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称加密算法密钥
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非对称加密算法公钥
|
||||||
|
*/
|
||||||
|
private String publicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非对称加密算法私钥
|
||||||
|
*/
|
||||||
|
private String privateKey;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKey(String publicKey) {
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateKey() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(String privateKey) {
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import org.apache.ibatis.plugin.*;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.enums.Algorithm;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段解密拦截器
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMyBatisInterceptor implements Interceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有字符串类型、需要加/解密的、有值字段
|
||||||
|
*
|
||||||
|
* @param obj 对象
|
||||||
|
* @return 字段列表
|
||||||
|
*/
|
||||||
|
public List<Field> getEncryptFields(Object obj) {
|
||||||
|
if (null == obj) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Arrays.stream(ReflectUtil.getFields(obj.getClass()))
|
||||||
|
.filter(field -> String.class.equals(field.getType()))
|
||||||
|
.filter(field -> null != field.getAnnotation(FieldEncrypt.class))
|
||||||
|
.filter(field -> null != ReflectUtil.getFieldValue(obj, field))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段加/解密处理器
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @return 加/解密处理器
|
||||||
|
*/
|
||||||
|
public IEncryptor getEncryptor(Field field) {
|
||||||
|
FieldEncrypt fieldEncrypt = field.getAnnotation(FieldEncrypt.class);
|
||||||
|
Class<? extends IEncryptor> encryptorClass = fieldEncrypt.encryptor();
|
||||||
|
// 使用预定义加/解密处理器
|
||||||
|
if (encryptorClass == IEncryptor.class) {
|
||||||
|
Algorithm algorithm = fieldEncrypt.value();
|
||||||
|
return ReflectUtil.newInstance(algorithm.getEncryptor());
|
||||||
|
}
|
||||||
|
// 使用自定义加/解密处理器
|
||||||
|
return SpringUtil.getBean(encryptorClass);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import org.apache.ibatis.executor.resultset.ResultSetHandler;
|
||||||
|
import org.apache.ibatis.plugin.*;
|
||||||
|
import org.apache.ibatis.type.SimpleTypeRegistry;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段解密拦截器
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
|
||||||
|
public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor {
|
||||||
|
|
||||||
|
private CryptoProperties properties;
|
||||||
|
|
||||||
|
public MyBatisDecryptInterceptor(CryptoProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyBatisDecryptInterceptor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object intercept(Invocation invocation) throws Throwable {
|
||||||
|
Object obj = invocation.proceed();
|
||||||
|
if (null == obj || !(invocation.getTarget() instanceof ResultSetHandler)) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
List<?> resultList = (List<?>)obj;
|
||||||
|
for (Object result : resultList) {
|
||||||
|
// String、Integer、Long 等简单类型对象无需处理
|
||||||
|
if (SimpleTypeRegistry.isSimpleType(result.getClass())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 获取所有字符串类型、需要解密的、有值字段
|
||||||
|
List<Field> fieldList = super.getEncryptFields(result);
|
||||||
|
// 解密处理
|
||||||
|
for (Field field : fieldList) {
|
||||||
|
IEncryptor encryptor = super.getEncryptor(field);
|
||||||
|
Object fieldValue = ReflectUtil.getFieldValue(result, field);
|
||||||
|
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
|
||||||
|
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class)
|
||||||
|
.password(), properties.getPassword());
|
||||||
|
String ciphertext = encryptor.decrypt(fieldValue.toString(), password, properties.getPrivateKey());
|
||||||
|
ReflectUtil.setFieldValue(result, field, ciphertext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||||
|
import org.apache.ibatis.executor.Executor;
|
||||||
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
|
import org.apache.ibatis.mapping.SqlCommandType;
|
||||||
|
import org.apache.ibatis.plugin.*;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段加密拦截器
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}),})
|
||||||
|
public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
|
||||||
|
|
||||||
|
private CryptoProperties properties;
|
||||||
|
|
||||||
|
public MyBatisEncryptInterceptor(CryptoProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyBatisEncryptInterceptor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object intercept(Invocation invocation) throws Throwable {
|
||||||
|
Object[] args = invocation.getArgs();
|
||||||
|
MappedStatement mappedStatement = (MappedStatement)args[0];
|
||||||
|
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
|
||||||
|
if (!(SqlCommandType.UPDATE == sqlCommandType || SqlCommandType.INSERT == sqlCommandType)) {
|
||||||
|
return invocation.proceed();
|
||||||
|
}
|
||||||
|
Object obj = args[1];
|
||||||
|
// 兼容 MyBatis Plus 封装的 update 相关方法,updateById、update
|
||||||
|
if (obj instanceof Map map) {
|
||||||
|
Object entity = map.get(Constants.ENTITY);
|
||||||
|
this.doEncrypt(this.getEncryptFields(entity), entity);
|
||||||
|
} else {
|
||||||
|
this.doEncrypt(this.getEncryptFields(obj), obj);
|
||||||
|
}
|
||||||
|
return invocation.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理加密
|
||||||
|
*
|
||||||
|
* @param fieldList 加密字段列表
|
||||||
|
* @param entity 实体
|
||||||
|
* @throws Exception /
|
||||||
|
*/
|
||||||
|
private void doEncrypt(List<Field> fieldList, Object entity) throws Exception {
|
||||||
|
for (Field field : fieldList) {
|
||||||
|
IEncryptor encryptor = super.getEncryptor(field);
|
||||||
|
Object fieldValue = ReflectUtil.getFieldValue(entity, field);
|
||||||
|
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
|
||||||
|
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class).password(), properties
|
||||||
|
.getPassword());
|
||||||
|
String ciphertext = encryptor.encrypt(fieldValue.toString(), password, properties.getPublicKey());
|
||||||
|
ReflectUtil.setFieldValue(entity, field, ciphertext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.symmetric.AES;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES(Advanced Encryption Standard) 加/解密处理器
|
||||||
|
* <p>
|
||||||
|
* 美国国家标准与技术研究院(NIST)采纳的对称加密算法标准,提供128位、192位和256位三种密钥长度,以高效和安全性著称。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public class AesEncryptor implements IEncryptor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String encrypt(String plaintext, String password, String publicKey) throws Exception {
|
||||||
|
AES aes = SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return aes.encryptHex(plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String decrypt(String ciphertext, String password, String privateKey) throws Exception {
|
||||||
|
AES aes = SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return aes.decryptStr(ciphertext);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64 加/解密处理器
|
||||||
|
* <p>
|
||||||
|
* 一种用于编码二进制数据到文本格式的算法,常用于邮件附件、网页传输等场合,但它不是一种加密算法,只提供数据的编码和解码,不保证数据的安全性。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public class Base64Encryptor implements IEncryptor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String encrypt(String plaintext, String password, String publicKey) throws Exception {
|
||||||
|
return Base64.encode(plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String decrypt(String ciphertext, String password, String privateKey) throws Exception {
|
||||||
|
return Base64.decodeStr(ciphertext);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加/解密接口
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public interface IEncryptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param plaintext 明文
|
||||||
|
* @param password 对称加密算法密钥
|
||||||
|
* @param publicKey 非对称加密算法公钥
|
||||||
|
* @return 加密后的文本
|
||||||
|
* @throws Exception /
|
||||||
|
*/
|
||||||
|
String encrypt(String plaintext, String password, String publicKey) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param ciphertext 密文
|
||||||
|
* @param password 对称加密算法密钥
|
||||||
|
* @param privateKey 非对称加密算法私钥
|
||||||
|
* @return 解密后的文本
|
||||||
|
* @throws Exception /
|
||||||
|
*/
|
||||||
|
String decrypt(String ciphertext, String password, String privateKey) throws Exception;
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA 加/解密处理器
|
||||||
|
* <p>
|
||||||
|
* 非对称加密算法,由罗纳德·李维斯特(Ron Rivest)、阿迪·沙米尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)于1977年提出,安全性基于大数因子分解问题的困难性。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public class RsaEncryptor implements IEncryptor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String encrypt(String plaintext, String password, String publicKey) throws Exception {
|
||||||
|
return Base64.encode(SecureUtil.rsa(null, publicKey).encrypt(plaintext, KeyType.PublicKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String decrypt(String ciphertext, String password, String privateKey) throws Exception {
|
||||||
|
return new String(SecureUtil.rsa(privateKey, null).decrypt(Base64.decode(ciphertext), KeyType.PrivateKey));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.security.crypto.enums;
|
||||||
|
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.AesEncryptor;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.Base64Encryptor;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||||
|
import top.charles7c.continew.starter.security.crypto.encryptor.RsaEncryptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密/解密算法枚举
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public enum Algorithm {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES
|
||||||
|
*/
|
||||||
|
AES(AesEncryptor.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA
|
||||||
|
*/
|
||||||
|
RSA(RsaEncryptor.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64
|
||||||
|
*/
|
||||||
|
BASE64(Base64Encryptor.class),;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密/解密处理器
|
||||||
|
*/
|
||||||
|
private final Class<? extends IEncryptor> encryptor;
|
||||||
|
|
||||||
|
Algorithm(Class<? extends IEncryptor> encryptor) {
|
||||||
|
this.encryptor = encryptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends IEncryptor> getEncryptor() {
|
||||||
|
return encryptor;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
top.charles7c.continew.starter.security.crypto.autoconfigure.CryptoAutoConfiguration
|
@@ -16,6 +16,7 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>continew-starter-security-password</module>
|
<module>continew-starter-security-password</module>
|
||||||
<module>continew-starter-security-mask</module>
|
<module>continew-starter-security-mask</module>
|
||||||
|
<module>continew-starter-security-crypto</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
Reference in New Issue
Block a user