Compare commits
2 Commits
852f52a2b3
...
c96663187e
| Author | SHA1 | Date |
|---|---|---|
|
|
c96663187e | |
|
|
1924cc6cb3 |
|
|
@ -75,4 +75,10 @@ public class Const implements Serializable {
|
||||||
public static final String ADMIN_USER_DEPT_CACHE_NAME = "ADMIN:USER:DEPT:CACHE:";
|
public static final String ADMIN_USER_DEPT_CACHE_NAME = "ADMIN:USER:DEPT:CACHE:";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密前缀
|
||||||
|
*/
|
||||||
|
public static final String ENCRYPTED_PREFIX = "ENCRYPTED:";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||||
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
|
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
|
||||||
|
import com.kakarote.core.security.converter.SensitiveDataConverter;
|
||||||
import com.kakarote.core.utils.BaseUtil;
|
import com.kakarote.core.utils.BaseUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.ibatis.type.TypeHandlerRegistry;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
|
@ -22,11 +24,22 @@ public class MybatisPlusConfig {
|
||||||
return paginationInterceptor;
|
return paginationInterceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Bean
|
||||||
|
// public ConfigurationCustomizer configurationCustomizer() {
|
||||||
|
// return i -> i.setObjectWrapperFactory(new MybatisMapWrapperFactory());
|
||||||
|
// }
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ConfigurationCustomizer configurationCustomizer() {
|
public ConfigurationCustomizer configurationCustomizer() {
|
||||||
return i -> i.setObjectWrapperFactory(new MybatisMapWrapperFactory());
|
return i -> {
|
||||||
|
i.setObjectWrapperFactory(new MybatisMapWrapperFactory());
|
||||||
|
// 注册敏感数据类型处理器
|
||||||
|
TypeHandlerRegistry registry = i.getTypeHandlerRegistry();
|
||||||
|
registry.register(SensitiveDataConverter.class);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public IdentifierGenerator idGenerator() {
|
public IdentifierGenerator idGenerator() {
|
||||||
return new CustomIdGenerator();
|
return new CustomIdGenerator();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
package com.kakarote.core.security;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.GCMParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class EncryptionService {
|
||||||
|
// AES-GCM参数配置
|
||||||
|
private static final int GCM_IV_LENGTH = 12;
|
||||||
|
private static final int GCM_TAG_LENGTH = 16;
|
||||||
|
private static final String AES_ALGORITHM = "AES/GCM/NoPadding";
|
||||||
|
private static final String AES_KEY_ALGORITHM = "AES";
|
||||||
|
|
||||||
|
// 系统主密钥(生产环境应从KMS获取)
|
||||||
|
@Value("${encryption.system-key}")
|
||||||
|
private String systemKey;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 注册BouncyCastle加密提供者
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密
|
||||||
|
* @param plaintext 明文
|
||||||
|
* @return Base64编码的密文
|
||||||
|
*/
|
||||||
|
public String encryptAes(String plaintext) {
|
||||||
|
try {
|
||||||
|
// 生成随机IV
|
||||||
|
byte[] iv = new byte[GCM_IV_LENGTH];
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
random.nextBytes(iv);
|
||||||
|
|
||||||
|
// 初始化密钥
|
||||||
|
SecretKey secretKey = new SecretKeySpec(Base64.getDecoder().decode(systemKey), AES_KEY_ALGORITHM);
|
||||||
|
|
||||||
|
// 初始化加密器
|
||||||
|
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
|
||||||
|
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
|
||||||
|
|
||||||
|
// 执行加密
|
||||||
|
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
// 组合IV和密文
|
||||||
|
byte[] result = new byte[iv.length + ciphertext.length];
|
||||||
|
System.arraycopy(iv, 0, result, 0, iv.length);
|
||||||
|
System.arraycopy(ciphertext, 0, result, iv.length, ciphertext.length);
|
||||||
|
|
||||||
|
return Base64.getEncoder().encodeToString(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("AES加密失败", e);
|
||||||
|
throw new SecurityException("数据加密失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
* @param ciphertext Base64编码的密文
|
||||||
|
* @return 明文
|
||||||
|
*/
|
||||||
|
public String decryptAes(String ciphertext) {
|
||||||
|
try {
|
||||||
|
// 解码Base64密文
|
||||||
|
byte[] decoded = Base64.getDecoder().decode(ciphertext);
|
||||||
|
|
||||||
|
// 提取IV
|
||||||
|
byte[] iv = new byte[GCM_IV_LENGTH];
|
||||||
|
System.arraycopy(decoded, 0, iv, 0, iv.length);
|
||||||
|
|
||||||
|
// 提取实际密文
|
||||||
|
byte[] encryptedData = new byte[decoded.length - iv.length];
|
||||||
|
System.arraycopy(decoded, iv.length, encryptedData, 0, encryptedData.length);
|
||||||
|
|
||||||
|
// 初始化密钥
|
||||||
|
SecretKey secretKey = new SecretKeySpec(Base64.getDecoder().decode(systemKey), AES_KEY_ALGORITHM);
|
||||||
|
|
||||||
|
// 初始化解密器
|
||||||
|
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
|
||||||
|
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
|
||||||
|
|
||||||
|
// 执行解密
|
||||||
|
byte[] plaintext = cipher.doFinal(encryptedData);
|
||||||
|
return new String(plaintext, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("AES解密失败", e);
|
||||||
|
throw new SecurityException("数据解密失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.kakarote.core.security;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class KeyManager {
|
||||||
|
// 密钥版本
|
||||||
|
@Value("${encryption.key-version:V1}")
|
||||||
|
private String keyVersion;
|
||||||
|
|
||||||
|
// 主密钥(生产环境应从KMS获取)
|
||||||
|
@Value("${encryption.system-key}")
|
||||||
|
private String systemKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成新的AES密钥
|
||||||
|
* @return Base64编码的密钥
|
||||||
|
*/
|
||||||
|
public String generateNewAesKey() {
|
||||||
|
try {
|
||||||
|
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
|
||||||
|
keyGenerator.init(256, new SecureRandom());
|
||||||
|
SecretKey secretKey = keyGenerator.generateKey();
|
||||||
|
return Base64.getEncoder().encodeToString(secretKey.getEncoded());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("生成AES密钥失败", e);
|
||||||
|
throw new SecurityException("密钥生成失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密钥轮换管理
|
||||||
|
*/
|
||||||
|
public void rotateKey() {
|
||||||
|
// 1. 生成新密钥
|
||||||
|
String newKey = generateNewAesKey();
|
||||||
|
|
||||||
|
// 2. 记录密钥版本
|
||||||
|
String newVersion = "V" + (Integer.parseInt(keyVersion.substring(1)) + 1);
|
||||||
|
|
||||||
|
// 3. 将新密钥存储到安全位置
|
||||||
|
// ... 密钥存储逻辑 ...
|
||||||
|
|
||||||
|
// 4. 异步更新数据库中所有旧版本加密数据
|
||||||
|
// ... 数据迁移逻辑 ...
|
||||||
|
|
||||||
|
log.info("密钥轮换完成,新版本: {}", newVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
package com.kakarote.core.security.converter;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||||
|
import com.kakarote.core.common.Const;
|
||||||
|
import com.kakarote.core.security.EncryptionService;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||||
|
import org.apache.ibatis.type.MappedTypes;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
@MappedTypes({String.class})
|
||||||
|
@MappedJdbcTypes({JdbcType.VARCHAR})
|
||||||
|
@Component
|
||||||
|
public class SensitiveDataConverter extends AbstractJsonTypeHandler<String> implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
private EncryptionService encryptionService;
|
||||||
|
|
||||||
|
// 无参构造函数,供MyBatis使用
|
||||||
|
public SensitiveDataConverter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带参构造函数,供Spring使用
|
||||||
|
public SensitiveDataConverter(EncryptionService encryptionService) {
|
||||||
|
this.encryptionService = encryptionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
SensitiveDataConverter.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EncryptionService getEncryptionService() {
|
||||||
|
if (encryptionService == null) {
|
||||||
|
encryptionService = applicationContext.getBean(EncryptionService.class);
|
||||||
|
}
|
||||||
|
return encryptionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
|
||||||
|
if (s != null && !s.isEmpty() && !s.startsWith(Const.ENCRYPTED_PREFIX)) {
|
||||||
|
s = getEncryptionService().encryptAes(s);
|
||||||
|
}
|
||||||
|
preparedStatement.setString(i, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNullableResult(ResultSet resultSet, String s) throws SQLException {
|
||||||
|
String value = resultSet.getString(s);
|
||||||
|
if (value != null && value.startsWith(Const.ENCRYPTED_PREFIX)) {
|
||||||
|
value = getEncryptionService().decryptAes(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
|
||||||
|
String value = resultSet.getString(i);
|
||||||
|
if (value != null && value.startsWith(Const.ENCRYPTED_PREFIX)) {
|
||||||
|
value = getEncryptionService().decryptAes(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
|
||||||
|
String value = callableStatement.getString(i);
|
||||||
|
if (value != null && value.startsWith(Const.ENCRYPTED_PREFIX)) {
|
||||||
|
value = getEncryptionService().decryptAes(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String parse(String json) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toJson(String obj) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ jetcache:
|
||||||
maxTotal: 50
|
maxTotal: 50
|
||||||
host: ${spring.redis.host}
|
host: ${spring.redis.host}
|
||||||
port: ${spring.redis.port}
|
port: ${spring.redis.port}
|
||||||
password: ${spring.redis.password}
|
password:
|
||||||
expireAfterWriteInMillis: 1800000
|
expireAfterWriteInMillis: 1800000
|
||||||
|
|
||||||
crm:
|
crm:
|
||||||
|
|
@ -86,4 +86,9 @@ crm:
|
||||||
bucketName:
|
bucketName:
|
||||||
0:
|
0:
|
||||||
1:
|
1:
|
||||||
|
encryption:
|
||||||
|
system-key: AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=
|
||||||
|
key-version: V1
|
||||||
|
mybatis:
|
||||||
|
type-handlers-package: com.kakarote.core.security.converter
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,7 @@ import com.kakarote.crm.entity.VO.CrmDataCheckVO;
|
||||||
import com.kakarote.crm.entity.VO.CrmInfoNumVO;
|
import com.kakarote.crm.entity.VO.CrmInfoNumVO;
|
||||||
import com.kakarote.crm.entity.VO.CrmMembersSelectVO;
|
import com.kakarote.crm.entity.VO.CrmMembersSelectVO;
|
||||||
import com.kakarote.crm.entity.VO.CrmModelFiledVO;
|
import com.kakarote.crm.entity.VO.CrmModelFiledVO;
|
||||||
import com.kakarote.crm.service.CrmUploadExcelService;
|
import com.kakarote.crm.service.*;
|
||||||
import com.kakarote.crm.service.ICrmCustomerService;
|
|
||||||
import com.kakarote.crm.service.ICrmOpenApiService;
|
|
||||||
import com.kakarote.crm.service.ICrmTeamMembersService;
|
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import io.swagger.annotations.ApiParam;
|
import io.swagger.annotations.ApiParam;
|
||||||
|
|
@ -75,6 +72,9 @@ public class CrmCustomerController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ICrmOpenApiService crmOpenApiService;
|
private ICrmOpenApiService crmOpenApiService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IBatchEncryptionService batchEncryptionService;
|
||||||
|
|
||||||
@PostMapping("/queryPageList")
|
@PostMapping("/queryPageList")
|
||||||
@ApiOperation("查询列表页数据")
|
@ApiOperation("查询列表页数据")
|
||||||
public Result<BasePage<Map<String, Object>>> queryPageList(@RequestBody CrmSearchBO search) {
|
public Result<BasePage<Map<String, Object>>> queryPageList(@RequestBody CrmSearchBO search) {
|
||||||
|
|
@ -553,5 +553,13 @@ public class CrmCustomerController {
|
||||||
String customerName = crmCustomerService.getCustomerName(customerId);
|
String customerName = crmCustomerService.getCustomerName(customerId);
|
||||||
return R.ok(customerName);
|
return R.ok(customerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/queryEncryptCustomerData")
|
||||||
|
@ApiOperation("批量加密客户数据")
|
||||||
|
@ParamAspect
|
||||||
|
public Result encryptCustomerData(@RequestParam(defaultValue = "1000") int pageSize) {
|
||||||
|
batchEncryptionService.batchEncryptCustomerData(pageSize);
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package com.kakarote.crm.entity.PO;
|
package com.kakarote.crm.entity.PO;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.kakarote.core.security.converter.SensitiveDataConverter;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
@ -52,17 +54,33 @@ public class CrmCustomer implements Serializable {
|
||||||
private Integer contactsId;
|
private Integer contactsId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "手机")
|
@ApiModelProperty(value = "手机")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
private String mobile;
|
private String mobile;
|
||||||
|
|
||||||
@ApiModelProperty(value = "电话")
|
@ApiModelProperty(value = "电话")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
private String telephone;
|
private String telephone;
|
||||||
|
|
||||||
@ApiModelProperty(value = "网址")
|
|
||||||
private String website;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "邮箱")
|
@ApiModelProperty(value = "邮箱")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "省市区")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "详细地址")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
|
private String detailAddress;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "地理位置经度")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
|
private String lng;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "地理位置维度")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
|
private String lat;
|
||||||
|
|
||||||
@ApiModelProperty(value = "备注")
|
@ApiModelProperty(value = "备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
|
@ -73,21 +91,11 @@ public class CrmCustomer implements Serializable {
|
||||||
@ApiModelProperty(value = "负责人ID")
|
@ApiModelProperty(value = "负责人ID")
|
||||||
private Long ownerUserId;
|
private Long ownerUserId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "省市区")
|
|
||||||
private String address;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "定位信息")
|
@ApiModelProperty(value = "定位信息")
|
||||||
|
@TableField(typeHandler = SensitiveDataConverter.class, jdbcType = JdbcType.VARCHAR)
|
||||||
private String location;
|
private String location;
|
||||||
|
|
||||||
@ApiModelProperty(value = "详细地址")
|
|
||||||
private String detailAddress;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "地理位置经度")
|
|
||||||
private String lng;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "地理位置维度")
|
|
||||||
private String lat;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "创建时间")
|
@ApiModelProperty(value = "创建时间")
|
||||||
@TableField(fill = FieldFill.INSERT)
|
@TableField(fill = FieldFill.INSERT)
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.kakarote.crm.service;
|
||||||
|
|
||||||
|
import com.kakarote.core.servlet.BaseService;
|
||||||
|
|
||||||
|
public interface IBatchEncryptionService {
|
||||||
|
/**
|
||||||
|
* 批量加密客户数据
|
||||||
|
* @param pageSize 每页处理数量
|
||||||
|
*/
|
||||||
|
void batchEncryptCustomerData(int pageSize);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.kakarote.crm.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.kakarote.core.common.Const;
|
||||||
|
import com.kakarote.core.security.converter.SensitiveDataConverter;
|
||||||
|
import com.kakarote.core.security.EncryptionService;
|
||||||
|
import com.kakarote.crm.entity.PO.CrmCustomer;
|
||||||
|
import com.kakarote.crm.mapper.CrmCustomerMapper;
|
||||||
|
import com.kakarote.crm.service.IBatchEncryptionService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class BatchEncryptionServiceImpl implements IBatchEncryptionService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CrmCustomerMapper customerMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EncryptionService encryptionService;
|
||||||
|
|
||||||
|
// 我们可以移除不需要的SensitiveDataConverter依赖
|
||||||
|
// @Autowired
|
||||||
|
// private SensitiveDataConverter sensitiveDataConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量加密客户数据
|
||||||
|
* @param pageSize 每页处理数量
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void batchEncryptCustomerData(int pageSize) {
|
||||||
|
int currentPage = 1;
|
||||||
|
boolean hasMoreData = true;
|
||||||
|
|
||||||
|
while (hasMoreData) {
|
||||||
|
// 分页查询客户数据
|
||||||
|
IPage<CrmCustomer> page = new Page<>(currentPage, pageSize);
|
||||||
|
LambdaQueryWrapper<CrmCustomer> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
// 可以添加条件来过滤未加密的数据
|
||||||
|
// wrapper.isNull(CrmCustomer::getEncryptedFlag);
|
||||||
|
|
||||||
|
IPage<CrmCustomer> result = customerMapper.selectPage(page, wrapper);
|
||||||
|
List<CrmCustomer> customerList = result.getRecords();
|
||||||
|
|
||||||
|
if (customerList.isEmpty()) {
|
||||||
|
hasMoreData = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密每个客户的敏感数据
|
||||||
|
for (CrmCustomer customer : customerList) {
|
||||||
|
// 加密敏感字段
|
||||||
|
if (customer.getMobile() != null) {
|
||||||
|
customer.setMobile(encryptionService.encryptAes(customer.getMobile()));
|
||||||
|
}
|
||||||
|
if (customer.getTelephone() != null) {
|
||||||
|
customer.setTelephone(encryptionService.encryptAes(customer.getTelephone()));
|
||||||
|
}
|
||||||
|
if (customer.getEmail() != null) {
|
||||||
|
customer.setEmail(encryptionService.encryptAes(customer.getEmail()));
|
||||||
|
}
|
||||||
|
if (customer.getAddress() != null) {
|
||||||
|
customer.setAddress(encryptionService.encryptAes(customer.getAddress()));
|
||||||
|
}
|
||||||
|
if (customer.getDetailAddress() != null) {
|
||||||
|
customer.setDetailAddress(encryptionService.encryptAes(customer.getDetailAddress()));
|
||||||
|
}
|
||||||
|
if (customer.getLocation() != null) {
|
||||||
|
customer.setLocation(encryptionService.encryptAes(customer.getLocation()));
|
||||||
|
}
|
||||||
|
// 标记为已加密
|
||||||
|
// customer.setEncryptedFlag(1);
|
||||||
|
|
||||||
|
// 更新客户数据
|
||||||
|
customerMapper.updateById(customer);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPage++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ import com.kakarote.core.feign.crm.entity.QueryEventCrmPageBO;
|
||||||
import com.kakarote.core.feign.crm.entity.SimpleCrmEntity;
|
import com.kakarote.core.feign.crm.entity.SimpleCrmEntity;
|
||||||
import com.kakarote.core.field.FieldService;
|
import com.kakarote.core.field.FieldService;
|
||||||
import com.kakarote.core.redis.Redis;
|
import com.kakarote.core.redis.Redis;
|
||||||
|
import com.kakarote.core.security.EncryptionService;
|
||||||
import com.kakarote.core.servlet.ApplicationContextHolder;
|
import com.kakarote.core.servlet.ApplicationContextHolder;
|
||||||
import com.kakarote.core.servlet.BaseServiceImpl;
|
import com.kakarote.core.servlet.BaseServiceImpl;
|
||||||
import com.kakarote.core.servlet.upload.FileEntity;
|
import com.kakarote.core.servlet.upload.FileEntity;
|
||||||
|
|
@ -127,6 +128,8 @@ public class CrmCustomerServiceImpl extends BaseServiceImpl<CrmCustomerMapper, C
|
||||||
@Autowired
|
@Autowired
|
||||||
private FieldService fieldService;
|
private FieldService fieldService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EncryptionService encryptionService;
|
||||||
/**
|
/**
|
||||||
* 查询字段配置
|
* 查询字段配置
|
||||||
*
|
*
|
||||||
|
|
@ -331,6 +334,8 @@ public class CrmCustomerServiceImpl extends BaseServiceImpl<CrmCustomerMapper, C
|
||||||
CrmModel crmModel;
|
CrmModel crmModel;
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
crmModel = getBaseMapper().queryById(id, UserUtil.getUserId());
|
crmModel = getBaseMapper().queryById(id, UserUtil.getUserId());
|
||||||
|
// 添加解密逻辑
|
||||||
|
decryptSensitiveData(crmModel);
|
||||||
crmModel.setLabel(CrmEnum.CUSTOMER.getType());
|
crmModel.setLabel(CrmEnum.CUSTOMER.getType());
|
||||||
crmModel.setOwnerUserName(UserCacheUtil.getUserName(crmModel.getOwnerUserId()));
|
crmModel.setOwnerUserName(UserCacheUtil.getUserName(crmModel.getOwnerUserId()));
|
||||||
crmCustomerDataService.setDataByBatchId(crmModel);
|
crmCustomerDataService.setDataByBatchId(crmModel);
|
||||||
|
|
@ -369,6 +374,17 @@ public class CrmCustomerServiceImpl extends BaseServiceImpl<CrmCustomerMapper, C
|
||||||
return crmModel;
|
return crmModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void decryptSensitiveData(CrmModel model) {
|
||||||
|
String[] sensitiveFields = {"mobile", "email", "idCard", "bankCard"};
|
||||||
|
for (String field : sensitiveFields) {
|
||||||
|
Object value = model.get(field);
|
||||||
|
if (value instanceof String) {
|
||||||
|
model.put(field, encryptionService.decryptAes((String) value));
|
||||||
|
System.out.println(encryptionService.decryptAes((String) value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存或新增信息
|
* 保存或新增信息
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import com.kakarote.core.feign.admin.entity.SimpleUser;
|
||||||
import com.kakarote.core.feign.admin.service.AdminService;
|
import com.kakarote.core.feign.admin.service.AdminService;
|
||||||
import com.kakarote.core.feign.crm.entity.BiAuthority;
|
import com.kakarote.core.feign.crm.entity.BiAuthority;
|
||||||
import com.kakarote.core.feign.crm.entity.BiParams;
|
import com.kakarote.core.feign.crm.entity.BiParams;
|
||||||
import com.kakarote.core.feign.crm.service.CrmUserAnalyseService;
|
|
||||||
import com.kakarote.core.servlet.ApplicationContextHolder;
|
import com.kakarote.core.servlet.ApplicationContextHolder;
|
||||||
import com.kakarote.core.utils.BiTimeUtil;
|
import com.kakarote.core.utils.BiTimeUtil;
|
||||||
import com.kakarote.core.utils.UserCacheUtil;
|
import com.kakarote.core.utils.UserCacheUtil;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import cn.hutool.core.date.DateUnit;
|
||||||
import cn.hutool.core.lang.UUID;
|
import cn.hutool.core.lang.UUID;
|
||||||
import com.aliyun.oss.ServiceException;
|
import com.aliyun.oss.ServiceException;
|
||||||
import com.ctc.wstx.util.DataUtil;
|
import com.ctc.wstx.util.DataUtil;
|
||||||
import com.foresee.platform.api.exception.PlatformApiException;
|
//import com.foresee.platform.api.exception.PlatformApiException;
|
||||||
import com.kakarote.core.exception.CrmException;
|
import com.kakarote.core.exception.CrmException;
|
||||||
import com.kakarote.crm.util.AecUtils;
|
import com.kakarote.crm.util.AecUtils;
|
||||||
import com.kakarote.crm.util.xml.XmlUtils;
|
import com.kakarote.crm.util.xml.XmlUtils;
|
||||||
|
|
@ -80,9 +80,6 @@ public class WebServiceUtil {
|
||||||
}else {
|
}else {
|
||||||
return XmlUtils.xmlToJson(ss);
|
return XmlUtils.xmlToJson(ss);
|
||||||
}
|
}
|
||||||
}catch (PlatformApiException ex){
|
|
||||||
log.error("响应报文body解密失败:"+responseStr,ex);
|
|
||||||
throw new CrmException(500,"解密失败");
|
|
||||||
}catch (JAXBException ex){
|
}catch (JAXBException ex){
|
||||||
log.error("响应报文xml格式错误:"+responseStr,ex);
|
log.error("响应报文xml格式错误:"+responseStr,ex);
|
||||||
throw new CrmException(500,"xml格式错误");
|
throw new CrmException(500,"xml格式错误");
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.kakarote.crm;
|
package com.kakarote.crm;
|
||||||
|
|
||||||
|
import com.kakarote.core.security.EncryptionService;
|
||||||
import com.kakarote.crm.entity.PO.*;
|
import com.kakarote.crm.entity.PO.*;
|
||||||
import com.kakarote.crm.service.*;
|
import com.kakarote.crm.service.*;
|
||||||
import com.kakarote.crm.util.AecUtils;
|
import com.kakarote.crm.util.AecUtils;
|
||||||
|
|
@ -24,6 +25,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
public class testQyjxfp {
|
public class testQyjxfp {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
EncryptionService encryptionService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ICrmQyjxfpService iCrmQyjxfpService;
|
private ICrmQyjxfpService iCrmQyjxfpService;
|
||||||
|
|
||||||
|
|
@ -478,6 +482,13 @@ public class testQyjxfp {
|
||||||
|
|
||||||
System.out.println ("respJson = " + respJson);
|
System.out.println ("respJson = " + respJson);
|
||||||
}
|
}
|
||||||
|
@Test
|
||||||
|
public void encryptionService(){
|
||||||
|
String s = encryptionService.encryptAes("江西方欣信息技术有限公司(1)");
|
||||||
|
System.out.println("加密后数据:"+s);
|
||||||
|
String s1 = encryptionService.decryptAes(s);
|
||||||
|
System.out.println("解密数据:"+s1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,3 +55,7 @@ crm:
|
||||||
upgradeFile:
|
upgradeFile:
|
||||||
#升级文件地址
|
#升级文件地址
|
||||||
url: D:/工具/version.json
|
url: D:/工具/version.json
|
||||||
|
|
||||||
|
encryption:
|
||||||
|
system-key: AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=
|
||||||
|
key-version: V1
|
||||||
|
|
@ -11,7 +11,7 @@ spring:
|
||||||
pool:
|
pool:
|
||||||
max-active: 300
|
max-active: 300
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:${DATASOURCE_DBTYPE:mysql}://${DATASOURCE_HOST:127.0.0.1}:${DATASOURCE_PORT:3306}/wk_crm_single?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&serverTimezone=Asia/Shanghai&useAffectedRows=true
|
url: jdbc:${DATASOURCE_DBTYPE:mysql}://${DATASOURCE_HOST:127.0.0.1}:${DATASOURCE_PORT:3307}/wk_crm_single?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&serverTimezone=Asia/Shanghai&useAffectedRows=true
|
||||||
username: ${DATASOURCE_USERNAME:devuser}
|
username: ${DATASOURCE_USERNAME:devuser}
|
||||||
password: ${DATASOURCE_PASSWORD:ckly@9069&Uk}
|
password: ${DATASOURCE_PASSWORD:ckly@9069&Uk}
|
||||||
elasticsearch:
|
elasticsearch:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue