init
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?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>org.springblade</groupId>
|
||||
<artifactId>BladeX-Tool</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>blade-starter-mybatis-encrypt</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${project.parent.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<module.name>org.springblade.blade.starter.mybatis.encrypt</module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--Blade-->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
<!-- Auto -->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-auto</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.algorithm;
|
||||
|
||||
/**
|
||||
* 加密类型枚举
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public enum Algorithm {
|
||||
|
||||
/**
|
||||
* SM4国密加密
|
||||
*/
|
||||
SM4,
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
*/
|
||||
AES,
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*/
|
||||
DES,
|
||||
|
||||
/**
|
||||
* Base64编码(非加密,不建议用于安全要求高的场景)
|
||||
*/
|
||||
BASE64,
|
||||
|
||||
/**
|
||||
* 自定义加密
|
||||
*/
|
||||
CUSTOM
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.algorithm;
|
||||
|
||||
/**
|
||||
* 加密器统一接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface EncryptAlgorithm {
|
||||
|
||||
/**
|
||||
* 获取加密器类型
|
||||
*
|
||||
* @return 加密算法类型
|
||||
*/
|
||||
String type();
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param plainText 明文
|
||||
* @param secretKey 密钥
|
||||
* @return 密文
|
||||
*/
|
||||
String encrypt(String plainText, String secretKey);
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param cipherText 密文
|
||||
* @param secretKey 密钥
|
||||
* @return 明文
|
||||
*/
|
||||
String decrypt(String cipherText, String secretKey);
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.algorithm;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.mp.encrypt.props.EncryptProperties;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 加密器工厂
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class EncryptAlgorithmFactory {
|
||||
|
||||
/**
|
||||
* 加密器集合
|
||||
*/
|
||||
private final List<EncryptAlgorithm> algorithms;
|
||||
|
||||
/**
|
||||
* 配置属性
|
||||
*/
|
||||
private final EncryptProperties properties;
|
||||
|
||||
/**
|
||||
* 加密器缓存池,使用 ConcurrentHashMap 保证线程安全
|
||||
*/
|
||||
private static final Map<String, EncryptAlgorithm> ENCRYPTOR_POOL = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 根据算法类型获取加密器,如果缓存中不存在,则尝试创建
|
||||
*
|
||||
* @param algorithm 算法类型
|
||||
* @return 加密器实例
|
||||
*/
|
||||
public EncryptAlgorithm create(Algorithm algorithm) {
|
||||
return create(algorithm.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据算法类型获取加密器
|
||||
*
|
||||
* @param algorithmType 算法类型字符串
|
||||
* @return 加密器实例
|
||||
*/
|
||||
public EncryptAlgorithm create(String algorithmType) {
|
||||
// 使用 computeIfAbsent 实现延迟加载
|
||||
return ENCRYPTOR_POOL.computeIfAbsent(algorithmType, this::initializeAlgorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置获取默认加密器
|
||||
*
|
||||
* @return 默认加密器实例
|
||||
*/
|
||||
public EncryptAlgorithm create() {
|
||||
Algorithm algorithm = properties.getAlgorithm();
|
||||
if (algorithm == null) {
|
||||
algorithm = Algorithm.AES; // 默认使用AES
|
||||
}
|
||||
return create(algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化加密器
|
||||
*
|
||||
* @param algorithmType 算法类型
|
||||
* @return 初始化的加密器
|
||||
*/
|
||||
private EncryptAlgorithm initializeAlgorithm(String algorithmType) {
|
||||
return algorithms.stream()
|
||||
.filter(algorithm -> algorithm.type().equals(algorithmType))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException(
|
||||
String.format("Unsupported encryption algorithm: %s", algorithmType)));
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.algorithm.provider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.mp.encrypt.algorithm.Algorithm;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithm;
|
||||
import org.springblade.core.tool.utils.AesUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* AES加密器
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AesAlgorithm implements EncryptAlgorithm {
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return Algorithm.AES.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String plainText, String secretKey) {
|
||||
if (StringUtil.isBlank(plainText)) {
|
||||
return plainText;
|
||||
}
|
||||
return AesUtil.encryptToHex(plainText, secretKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String cipherText, String secretKey) {
|
||||
if (StringUtil.isBlank(cipherText)) {
|
||||
return cipherText;
|
||||
}
|
||||
try {
|
||||
return AesUtil.decryptFormHexToString(cipherText, secretKey);
|
||||
} catch (Exception e) {
|
||||
log.error("AES decryption failed, returning original value: {}", e.getMessage());
|
||||
return cipherText;
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.algorithm.provider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.mp.encrypt.algorithm.Algorithm;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithm;
|
||||
import org.springblade.core.tool.utils.Base64Util;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Base64编码器(非真正加密,仅用于编码)
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class Base64Algorithm implements EncryptAlgorithm {
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return Algorithm.BASE64.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String plainText, String secretKey) {
|
||||
if (StringUtil.isBlank(plainText)) {
|
||||
return plainText;
|
||||
}
|
||||
// Base64不需要密钥
|
||||
return Base64Util.encode(plainText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String cipherText, String secretKey) {
|
||||
if (StringUtil.isBlank(cipherText)) {
|
||||
return cipherText;
|
||||
}
|
||||
try {
|
||||
// Base64不需要密钥
|
||||
return Base64Util.decode(cipherText);
|
||||
} catch (Exception e) {
|
||||
log.error("Base64 decoding failed, returning original value: {}", e.getMessage());
|
||||
return cipherText;
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.algorithm.provider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.mp.encrypt.algorithm.Algorithm;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithm;
|
||||
import org.springblade.core.tool.utils.DesUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* DES加密器
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DesAlgorithm implements EncryptAlgorithm {
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return Algorithm.DES.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String plainText, String secretKey) {
|
||||
if (StringUtil.isBlank(plainText)) {
|
||||
return plainText;
|
||||
}
|
||||
return DesUtil.encryptToHex(plainText, secretKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String cipherText, String secretKey) {
|
||||
if (StringUtil.isBlank(cipherText)) {
|
||||
return cipherText;
|
||||
}
|
||||
try {
|
||||
return DesUtil.decryptFormHex(cipherText, secretKey);
|
||||
} catch (Exception e) {
|
||||
log.error("DES decryption failed, returning original value: {}", e.getMessage());
|
||||
return cipherText;
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.algorithm.provider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.mp.encrypt.algorithm.Algorithm;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithm;
|
||||
import org.springblade.core.tool.utils.SM4Util;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* SM4国密加密器
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class Sm4Algorithm implements EncryptAlgorithm {
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return Algorithm.SM4.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String plainText, String secretKey) {
|
||||
if (StringUtil.isBlank(plainText)) {
|
||||
return plainText;
|
||||
}
|
||||
return SM4Util.encrypt(plainText, secretKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String cipherText, String secretKey) {
|
||||
if (StringUtil.isBlank(cipherText)) {
|
||||
return cipherText;
|
||||
}
|
||||
try {
|
||||
return SM4Util.decrypt(cipherText, secretKey);
|
||||
} catch (Exception e) {
|
||||
log.error("SM4 decryption failed, returning original value: {}", e.getMessage());
|
||||
return cipherText;
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.annotation;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import org.springblade.core.mp.encrypt.handler.EncryptTypeHandler;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Mybatis字段加密注解
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@TableField(typeHandler = EncryptTypeHandler.class)
|
||||
public @interface FieldEncrypt {
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.annotation;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Mybatis字段加密模糊查询注解
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
public @interface SearchableFieldEncrypt {
|
||||
|
||||
/**
|
||||
* 是否启用模糊查询支持
|
||||
* 当设置为true时,字段值会使用滑动窗口加密存储,支持like查询
|
||||
*
|
||||
* @return true-支持模糊查询,false-不支持模糊查询
|
||||
*/
|
||||
boolean enabled() default true;
|
||||
|
||||
/**
|
||||
* 模糊查询时,Entity的加密字段名称后缀
|
||||
*
|
||||
* @return 加密字段名称后缀,默认"Enc"
|
||||
*/
|
||||
String encName() default "Enc";
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithm;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithmFactory;
|
||||
import org.springblade.core.mp.encrypt.exception.EncryptException;
|
||||
import org.springblade.core.mp.encrypt.props.EncryptProperties;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 字段加密自动配置
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(prefix = EncryptProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class EncryptAutoConfiguration {
|
||||
|
||||
private final EncryptProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// 启动时验证配置
|
||||
if (StringUtil.isBlank(properties.getSecretKey())) {
|
||||
throw new EncryptException(
|
||||
"MyBatis field encryption is enabled but no secret key is configured. " +
|
||||
"Please configure 'blade.mybatis-plus.encrypt.secret-key' in application.yml " +
|
||||
"or disable encryption by setting 'blade.mybatis-plus.encrypt.enabled=false'");
|
||||
}
|
||||
log.info("MyBatis field encryption enabled successfully with algorithm: {}", properties.getAlgorithm());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EncryptAlgorithmFactory encryptAlgorithmFactory(List<EncryptAlgorithm> encryptors) {
|
||||
return new EncryptAlgorithmFactory(encryptors, properties);
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.config;
|
||||
|
||||
import org.springblade.core.launch.props.BladePropertySource;
|
||||
import org.springblade.core.mp.encrypt.props.EncryptProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
|
||||
/**
|
||||
* 字段加密自动配置
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(EncryptProperties.class)
|
||||
@BladePropertySource(value = "classpath:/blade-mybatis-encrypt.yml")
|
||||
public class EncryptConfiguration {
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 加密异常
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public class EncryptException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param message 错误消息
|
||||
*/
|
||||
public EncryptException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param message 错误消息
|
||||
* @param cause 原因
|
||||
*/
|
||||
public EncryptException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springblade.core.mp.encrypt.annotation.SearchableFieldEncrypt;
|
||||
import org.springblade.core.mp.encrypt.utils.SlidingUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 加密搜索字段自定义填充
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EncryptMetaObjectHandler implements MetaObjectHandler {
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
processEncryptFields(metaObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
processEncryptFields(metaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理带有SearchableEncryptField注解且开启模糊查询的字段
|
||||
*
|
||||
* @param metaObject 元对象
|
||||
*/
|
||||
private void processEncryptFields(MetaObject metaObject) {
|
||||
if (metaObject == null || metaObject.getOriginalObject() == null) {
|
||||
return;
|
||||
}
|
||||
Field[] declaredFields = metaObject.getOriginalObject().getClass().getDeclaredFields();
|
||||
Arrays.stream(declaredFields).forEach(field -> processField(field, metaObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个字段
|
||||
*
|
||||
* @param field 字段
|
||||
* @param metaObject 元对象
|
||||
*/
|
||||
private void processField(Field field, MetaObject metaObject) {
|
||||
Optional.ofNullable(field.getAnnotation(SearchableFieldEncrypt.class))
|
||||
.filter(SearchableFieldEncrypt::enabled)
|
||||
.ifPresent(annotation -> processSearchableField(field, annotation, metaObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理可搜索的加密字段
|
||||
*
|
||||
* @param field 字段
|
||||
* @param annotation 注解
|
||||
* @param metaObject 元对象
|
||||
*/
|
||||
private void processSearchableField(Field field, SearchableFieldEncrypt annotation, MetaObject metaObject) {
|
||||
String sourceFieldName = StringUtil.replace(field.getName(), annotation.encName(), StringPool.EMPTY);
|
||||
|
||||
Optional.ofNullable(this.getFieldValByName(sourceFieldName, metaObject))
|
||||
.filter(String.class::isInstance)
|
||||
.map(String.class::cast)
|
||||
.map(SlidingUtil::segment)
|
||||
.ifPresent(segmentedValue -> this.setFieldValByName(field.getName(), segmentedValue, metaObject));
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.handler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.springblade.core.mp.encrypt.utils.EncryptUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* MyBatis字段加密TypeHandler
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
public class EncryptTypeHandler extends BaseTypeHandler<String> {
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
|
||||
if (StringUtil.isBlank(parameter)) {
|
||||
ps.setString(i, parameter);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ps.setString(i, EncryptUtil.encrypt(parameter));
|
||||
} catch (Exception e) {
|
||||
log.error("Field encryption failed: {}", e.getMessage(), e);
|
||||
throw new SQLException("Field encryption failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String value = rs.getString(columnName);
|
||||
return EncryptUtil.decrypt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String value = rs.getString(columnIndex);
|
||||
return EncryptUtil.decrypt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String value = cs.getString(columnIndex);
|
||||
return EncryptUtil.decrypt(value);
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springblade.core.mp.encrypt.algorithm.Algorithm;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* 加密配置属性
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = EncryptProperties.PREFIX)
|
||||
public class EncryptProperties {
|
||||
public static final String PREFIX = "blade.mybatis-plus.encrypt";
|
||||
|
||||
/**
|
||||
* 是否启用字段加密功能
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 滑动窗口大小,用于字段加密的模糊查询
|
||||
*/
|
||||
private int windowSize = 3;
|
||||
|
||||
/**
|
||||
* 加密算法类型
|
||||
*/
|
||||
private Algorithm algorithm = Algorithm.AES;
|
||||
|
||||
/**
|
||||
* 加密算法密钥
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.support;
|
||||
|
||||
import org.springblade.core.launch.constant.TokenConstant;
|
||||
import org.springblade.core.mp.encrypt.wrapper.EncryptQueryWrapper;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.SqlKeyword;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.ClassUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 加密分页工具
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class EncryptCondition extends Condition {
|
||||
|
||||
/**
|
||||
* 获取mybatis plus中的QueryWrapper
|
||||
*
|
||||
* @param entity 实体
|
||||
* @param <T> 类型
|
||||
* @return QueryWrapper
|
||||
*/
|
||||
public static <T> EncryptQueryWrapper<T> getEncryptQueryWrapper(T entity) {
|
||||
return new EncryptQueryWrapper<>(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取mybatis plus中的QueryWrapper
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param clazz 实体类
|
||||
* @param <T> 类型
|
||||
* @return QueryWrapper
|
||||
*/
|
||||
public static <T> EncryptQueryWrapper<T> getEncryptQueryWrapper(Map<String, Object> query, Class<T> clazz) {
|
||||
Kv exclude = Kv.create().set(TokenConstant.AUTH_HEADER, TokenConstant.AUTH_HEADER)
|
||||
.set("current", "current").set("size", "size").set("ascs", "ascs").set("descs", "descs");
|
||||
return getEncryptQueryWrapper(query, exclude, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取mybatis plus中的QueryWrapper
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param exclude 排除的查询条件
|
||||
* @param clazz 实体类
|
||||
* @param <T> 类型
|
||||
* @return QueryWrapper
|
||||
*/
|
||||
public static <T> EncryptQueryWrapper<T> getEncryptQueryWrapper(Map<String, Object> query, Map<String, Object> exclude, Class<T> clazz) {
|
||||
// 移除 query 中在 exclude 里有的字段
|
||||
exclude.forEach((k, v) -> query.remove(k));
|
||||
// 获取 clazz 的所有字段名,构造成 Set
|
||||
Set<String> fieldNames = ClassUtil.getClassFieldNames(clazz);
|
||||
// 移除 query 中不属于 clazz 字段的键
|
||||
query.keySet().removeIf(key -> !fieldNames.contains(key.split(StringPool.UNDERSCORE)[0]));
|
||||
// 构造 QueryWrapper
|
||||
EncryptQueryWrapper<T> qw = new EncryptQueryWrapper<>();
|
||||
qw.setEntity(BeanUtil.newInstance(clazz));
|
||||
// 使用 SQL 关键字条件构建
|
||||
SqlKeyword.buildCondition(query, qw);
|
||||
// 返回构造类
|
||||
return qw;
|
||||
}
|
||||
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.mp.encrypt.algorithm.Algorithm;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithm;
|
||||
import org.springblade.core.mp.encrypt.algorithm.EncryptAlgorithmFactory;
|
||||
import org.springblade.core.mp.encrypt.exception.EncryptException;
|
||||
import org.springblade.core.mp.encrypt.props.EncryptProperties;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
/**
|
||||
* 加密工具类 - 提供统一的密钥管理和加密解密方法
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
public class EncryptUtil {
|
||||
|
||||
private static volatile EncryptProperties encryptProperties;
|
||||
private static volatile EncryptAlgorithmFactory encryptAlgorithmFactory;
|
||||
private static final int DEFAULT_WINDOW_SIZE = 3;
|
||||
|
||||
/**
|
||||
* 延迟加载获取加密配置属性
|
||||
*
|
||||
* @return 加密配置属性
|
||||
*/
|
||||
private static EncryptProperties getEncryptProperties() {
|
||||
if (encryptProperties == null) {
|
||||
synchronized (EncryptUtil.class) {
|
||||
if (encryptProperties == null) {
|
||||
encryptProperties = SpringUtil.getBean(EncryptProperties.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return encryptProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟加载获取加密器工厂
|
||||
*
|
||||
* @return 加密器工厂
|
||||
*/
|
||||
private static EncryptAlgorithmFactory getEncryptFactory() {
|
||||
if (encryptAlgorithmFactory == null) {
|
||||
synchronized (EncryptUtil.class) {
|
||||
if (encryptAlgorithmFactory == null) {
|
||||
encryptAlgorithmFactory = SpringUtil.getBean(EncryptAlgorithmFactory.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return encryptAlgorithmFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密算法
|
||||
*
|
||||
* @return 加密算法
|
||||
*/
|
||||
public static EncryptAlgorithm getAlgorithm() {
|
||||
EncryptProperties properties = getEncryptProperties();
|
||||
Algorithm algorithm = properties.getAlgorithm();
|
||||
return getEncryptFactory().create(algorithm.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密密钥
|
||||
*
|
||||
* @return 加密密钥
|
||||
*/
|
||||
public static String getSecretKey() {
|
||||
EncryptProperties properties = getEncryptProperties();
|
||||
String secretKey = properties.getSecretKey();
|
||||
if (StringUtil.isBlank(secretKey)) {
|
||||
throw new EncryptException(
|
||||
"Encryption key not configured. Please set 'blade.mybatis-plus.encrypt.secret-key' in configuration file");
|
||||
}
|
||||
if (secretKey.length() != 32) {
|
||||
throw new EncryptException(
|
||||
"Encryption key must be exactly 32 characters long, current length: " + secretKey.length());
|
||||
}
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取滑动窗口大小
|
||||
*
|
||||
* @return 滑动窗口大小
|
||||
*/
|
||||
public static int getWindowSize() {
|
||||
EncryptProperties properties = getEncryptProperties();
|
||||
return Func.toInt(properties.getWindowSize(), DEFAULT_WINDOW_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密字符串为Base64格式(使用配置的算法)
|
||||
*
|
||||
* @param plainText 明文
|
||||
* @return 加密后的Base64字符串
|
||||
*/
|
||||
public static String encrypt(String plainText) {
|
||||
if (StringUtil.isBlank(plainText)) {
|
||||
return plainText;
|
||||
}
|
||||
EncryptAlgorithm algorithm = getAlgorithm();
|
||||
return algorithm.encrypt(plainText, getSecretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密字符串为Base64格式(指定密钥)
|
||||
*
|
||||
* @param plainText 明文
|
||||
* @param secretKey 密钥
|
||||
* @return 加密后的Base64字符串
|
||||
*/
|
||||
public static String encrypt(String plainText, String secretKey) {
|
||||
if (StringUtil.isBlank(plainText)) {
|
||||
return plainText;
|
||||
}
|
||||
EncryptAlgorithm algorithm = getAlgorithm();
|
||||
return algorithm.encrypt(plainText, secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密Base64格式的密文(使用配置的算法)
|
||||
*
|
||||
* @param cipherText Base64格式的密文
|
||||
* @return 解密后的明文
|
||||
*/
|
||||
public static String decrypt(String cipherText) {
|
||||
if (StringUtil.isBlank(cipherText)) {
|
||||
return cipherText;
|
||||
}
|
||||
EncryptAlgorithm algorithm = getAlgorithm();
|
||||
return algorithm.decrypt(cipherText, getSecretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密Base64格式的密文(指定密钥)
|
||||
*
|
||||
* @param cipherText Base64格式的密文
|
||||
* @param secretKey 密钥
|
||||
* @return 解密后的明文
|
||||
*/
|
||||
public static String decrypt(String cipherText, String secretKey) {
|
||||
if (StringUtil.isBlank(cipherText)) {
|
||||
return cipherText;
|
||||
}
|
||||
EncryptAlgorithm algorithm = getAlgorithm();
|
||||
return algorithm.decrypt(cipherText, secretKey);
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.mp.encrypt.utils;
|
||||
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* 滑动窗口加密工具类
|
||||
* 用于支持加密字段的模糊查询
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public class SlidingUtil {
|
||||
|
||||
/**
|
||||
* 使用默认窗口大小进行滑动窗口加密
|
||||
*
|
||||
* @param text 原始文本
|
||||
* @param secretKey 加密密钥
|
||||
* @return 加密后的拼接字符串
|
||||
*/
|
||||
public static String segment(String text, String secretKey) {
|
||||
return segment(text, EncryptUtil.getWindowSize(), secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认窗口大小和默认密钥进行滑动窗口加密
|
||||
*
|
||||
* @param text 原始文本
|
||||
* @return 加密后的拼接字符串
|
||||
*/
|
||||
public static String segment(String text) {
|
||||
return segment(text, EncryptUtil.getWindowSize(), EncryptUtil.getSecretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定窗口大小和默认密钥进行滑动窗口加密
|
||||
*
|
||||
* @param text 原始文本
|
||||
* @param windowSize 窗口大小
|
||||
* @return 加密后的拼接字符串
|
||||
*/
|
||||
public static String segment(String text, int windowSize) {
|
||||
return segment(text, windowSize, EncryptUtil.getSecretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按滑动窗口方式将字符串分割为固定长度的子串并加密
|
||||
*
|
||||
* @param text 原始文本
|
||||
* @param windowSize 窗口大小
|
||||
* @param secretKey 加密密钥
|
||||
* @return 加密后的拼接字符串
|
||||
*/
|
||||
public static String segment(String text, int windowSize, String secretKey) {
|
||||
if (StringUtil.isBlank(text) || StringUtil.isBlank(secretKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果文本长度小于窗口大小,直接加密整个文本
|
||||
if (text.length() < windowSize || windowSize <= 0) {
|
||||
return EncryptUtil.encrypt(text, secretKey);
|
||||
}
|
||||
|
||||
int maxStartIndex = text.length() - windowSize;
|
||||
|
||||
// 使用 Stream 进行滑动窗口处理
|
||||
return IntStream.rangeClosed(0, maxStartIndex)
|
||||
.mapToObj(i -> text.substring(i, i + windowSize))
|
||||
.map(subStr -> EncryptUtil.encrypt(subStr, secretKey))
|
||||
.collect(Collectors.joining(StringPool.COMMA));
|
||||
}
|
||||
}
|
||||
+336
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2025, baomidou (jobob@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springblade.core.mp.encrypt.wrapper;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.core.conditions.SharedString;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import org.springblade.core.mp.encrypt.annotation.SearchableFieldEncrypt;
|
||||
import org.springblade.core.mp.encrypt.handler.EncryptTypeHandler;
|
||||
import org.springblade.core.mp.encrypt.utils.EncryptUtil;
|
||||
import org.springblade.core.mp.encrypt.utils.SlidingUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 自定义加密查询Wrapper
|
||||
* 自动对查询条件进行加密处理
|
||||
*
|
||||
* @author hubin miemie HCL BladeX
|
||||
*/
|
||||
public class EncryptLambdaQueryWrapper<T> extends LambdaQueryWrapper<T> {
|
||||
|
||||
private Class<T> entityClass;
|
||||
|
||||
private SharedString sqlSelect = new SharedString();
|
||||
|
||||
/**
|
||||
* 缓存字段是否需要加密的信息
|
||||
*/
|
||||
private static final Map<String, Boolean> FIELD_ENCRYPT_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
public EncryptLambdaQueryWrapper() {
|
||||
super();
|
||||
}
|
||||
|
||||
public EncryptLambdaQueryWrapper(T entity) {
|
||||
super.setEntity(entity);
|
||||
super.initNeed();
|
||||
}
|
||||
|
||||
public EncryptLambdaQueryWrapper(Class<T> entityClass) {
|
||||
super.setEntityClass(entityClass);
|
||||
super.initNeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于从 EncryptQueryWrapper 转换的构造方法
|
||||
*/
|
||||
EncryptLambdaQueryWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,
|
||||
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
|
||||
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
|
||||
super.setEntity(entity);
|
||||
super.setEntityClass(entityClass);
|
||||
this.paramNameSeq = paramNameSeq;
|
||||
this.paramNameValuePairs = paramNameValuePairs;
|
||||
this.expression = mergeSegments;
|
||||
this.sqlSelect = sqlSelect;
|
||||
this.paramAlias = paramAlias;
|
||||
this.lastSql = lastSql;
|
||||
this.sqlComment = sqlComment;
|
||||
this.sqlFirst = sqlFirst;
|
||||
this.entityClass = entityClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
|
||||
if (entityClass == null) {
|
||||
entityClass = getEntityClass();
|
||||
} else {
|
||||
setEntityClass(entityClass);
|
||||
}
|
||||
this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate));
|
||||
return typedThis;
|
||||
}
|
||||
|
||||
protected LambdaQueryWrapper<T> doSelect(boolean condition, List<SFunction<T, ?>> columns) {
|
||||
if (condition && CollectionUtils.isNotEmpty(columns)) {
|
||||
this.sqlSelect.setStringValue(columnsToString(false, columns));
|
||||
}
|
||||
return typedThis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSqlSelect() {
|
||||
return sqlSelect.getStringValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapper<T> eq(boolean condition, SFunction<T, ?> column, Object val) {
|
||||
if (condition && val != null) {
|
||||
Object encryptedVal = applyConditionalEncryption(column, val);
|
||||
return super.eq(true, column, encryptedVal);
|
||||
}
|
||||
return super.eq(condition, column, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapper<T> ne(boolean condition, SFunction<T, ?> column, Object val) {
|
||||
if (condition && val != null) {
|
||||
Object encryptedVal = applyConditionalEncryption(column, val);
|
||||
return super.ne(true, column, encryptedVal);
|
||||
}
|
||||
return super.ne(condition, column, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapper<T> in(boolean condition, SFunction<T, ?> column, Object... values) {
|
||||
if (condition && values != null && values.length > 0) {
|
||||
Object[] encryptedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
encryptedValues[i] = applyConditionalEncryption(column, values[i]);
|
||||
}
|
||||
return super.in(true, column, encryptedValues);
|
||||
}
|
||||
return super.in(condition, column, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询(使用滑动窗口加密)
|
||||
* 注意:需要配合特殊的数据库字段存储方式使用
|
||||
*/
|
||||
@Override
|
||||
public LambdaQueryWrapper<T> like(SFunction<T, ?> column, Object val) {
|
||||
return like(true, column, val, EncryptUtil.getWindowSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询(使用滑动窗口加密)
|
||||
* 注意:需要配合特殊的数据库字段存储方式使用
|
||||
*/
|
||||
@Override
|
||||
public LambdaQueryWrapper<T> like(boolean condition, SFunction<T, ?> column, Object val) {
|
||||
return like(condition, column, val, EncryptUtil.getWindowSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询(使用滑动窗口加密)
|
||||
*
|
||||
* @param condition 条件
|
||||
* @param column 列
|
||||
* @param val 值
|
||||
* @param windowSize 窗口大小
|
||||
*/
|
||||
public LambdaQueryWrapper<T> like(boolean condition, SFunction<T, ?> column, Object val, int windowSize) {
|
||||
if (condition && val != null && requiresEncryption(column)) {
|
||||
String strVal = val.toString();
|
||||
String encryptedVal = SlidingUtil.segment(strVal, windowSize);
|
||||
return super.like(true, column, encryptedVal);
|
||||
}
|
||||
return super.like(condition, column, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getEntityClass() {
|
||||
if (this.entityClass != null) {
|
||||
return this.entityClass;
|
||||
}
|
||||
return super.getEntityClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于生成嵌套 sql
|
||||
* <p>故 sqlSelect 不向下传递</p>
|
||||
*/
|
||||
@Override
|
||||
protected EncryptLambdaQueryWrapper<T> instance() {
|
||||
return new EncryptLambdaQueryWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,
|
||||
new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
sqlSelect.toNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对字段值进行条件性加密处理
|
||||
*
|
||||
* @param column 字段函数引用
|
||||
* @param value 待处理的值
|
||||
* @return 加密后的值或原值
|
||||
*/
|
||||
private Object applyConditionalEncryption(SFunction<T, ?> column, Object value) {
|
||||
if (value == null || StringUtil.isBlank(value.toString())) {
|
||||
return value;
|
||||
}
|
||||
Class<T> currentEntityClass = getEntityClassFromColumn(column);
|
||||
if (currentEntityClass == null) {
|
||||
return value;
|
||||
}
|
||||
return requiresEncryption(column)
|
||||
? EncryptUtil.encrypt(value.toString())
|
||||
: value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字段是否启用加密功能
|
||||
*
|
||||
* @param column 字段函数引用
|
||||
* @return true 如果字段需要加密,否则返回 false
|
||||
*/
|
||||
private boolean requiresEncryption(SFunction<T, ?> column) {
|
||||
Class<T> currentEntityClass = getEntityClassFromColumn(column);
|
||||
if (currentEntityClass == null) {
|
||||
return false;
|
||||
}
|
||||
String fieldName = resolveLambdaFieldName(column);
|
||||
if (fieldName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String cacheKey = buildCacheKey(fieldName);
|
||||
return FIELD_ENCRYPT_CACHE.computeIfAbsent(cacheKey, key ->
|
||||
detectEncryptionHandler(fieldName) || detectEncryptFieldAnnotation(fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建缓存键
|
||||
*/
|
||||
private String buildCacheKey(String fieldName) {
|
||||
return getEntityClass().getName() + StringPool.DOT + fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测字段是否配置了加密处理器
|
||||
*/
|
||||
private boolean detectEncryptionHandler(String fieldName) {
|
||||
try {
|
||||
Field field = getEntityClass().getDeclaredField(fieldName);
|
||||
TableField tableField = field.getAnnotation(TableField.class);
|
||||
|
||||
return tableField != null
|
||||
&& tableField.typeHandler() != null
|
||||
&& tableField.typeHandler().getName().contains(EncryptTypeHandler.class.getName());
|
||||
} catch (NoSuchFieldException | SecurityException e) {
|
||||
// 字段不存在或访问受限,默认不加密
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测字段是否配置了EncryptField注解且开启了模糊查询
|
||||
*
|
||||
* @param fieldName 字段名称
|
||||
* @return true 如果字段配置了EncryptField注解且开启了模糊查询,否则返回 false
|
||||
*/
|
||||
private boolean detectEncryptFieldAnnotation(String fieldName) {
|
||||
try {
|
||||
Field field = getEntityClass().getDeclaredField(fieldName);
|
||||
SearchableFieldEncrypt searchableFieldEncrypt = field.getAnnotation(SearchableFieldEncrypt.class);
|
||||
|
||||
return searchableFieldEncrypt != null && searchableFieldEncrypt.enabled();
|
||||
} catch (NoSuchFieldException | SecurityException e) {
|
||||
// 字段不存在或访问受限,默认不启用模糊查询
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析Lambda表达式获取字段名称
|
||||
*
|
||||
* @param column Lambda表达式函数引用
|
||||
* @return 字段名称,解析失败返回null
|
||||
*/
|
||||
private String resolveLambdaFieldName(SFunction<T, ?> column) {
|
||||
try {
|
||||
Method method = column.getClass().getDeclaredMethod("writeReplace");
|
||||
method.setAccessible(true);
|
||||
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(column);
|
||||
String methodName = serializedLambda.getImplMethodName();
|
||||
return extractFieldFromAccessor(methodName);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
// Lambda表达式解析失败
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从访问器方法名中提取字段名
|
||||
*
|
||||
* @param accessorName getter/setter方法名
|
||||
* @return 字段名
|
||||
*/
|
||||
private String extractFieldFromAccessor(String accessorName) {
|
||||
if (accessorName.startsWith("get") && accessorName.length() > 3) {
|
||||
return StringUtil.firstCharToLower(accessorName.substring(3));
|
||||
} else if (accessorName.startsWith("is") && accessorName.length() > 2) {
|
||||
return StringUtil.firstCharToLower(accessorName.substring(2));
|
||||
}
|
||||
return accessorName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过SFunction解析出实体类的Class
|
||||
*
|
||||
* @param column 字段函数引用
|
||||
* @return 实体类Class
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<T> getEntityClassFromColumn(SFunction<T, ?> column) {
|
||||
if (this.entityClass != null) {
|
||||
return this.entityClass;
|
||||
}
|
||||
this.entityClass = (Class<T>) LambdaUtils.extract(column).getInstantiatedClass();
|
||||
return this.entityClass;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2025, baomidou (jobob@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springblade.core.mp.encrypt.wrapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.SharedString;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 自定义加密查询Wrapper
|
||||
* 主要用于提供lambda()方法转换到EncryptLambdaQueryWrapper
|
||||
*
|
||||
* @author hubin miemie HCL BladeX
|
||||
*/
|
||||
public class EncryptQueryWrapper<T> extends QueryWrapper<T> {
|
||||
|
||||
public EncryptQueryWrapper() {
|
||||
super();
|
||||
}
|
||||
|
||||
public EncryptQueryWrapper(T entity) {
|
||||
super.setEntity(entity);
|
||||
super.initNeed();
|
||||
}
|
||||
|
||||
public EncryptQueryWrapper(Class<T> entityClass) {
|
||||
super.setEntityClass(entityClass);
|
||||
super.initNeed();
|
||||
}
|
||||
|
||||
public EncryptQueryWrapper(T entity, String... columns) {
|
||||
super.setEntity(entity);
|
||||
super.initNeed();
|
||||
this.select(columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 非对外公开的构造方法,只用于生产嵌套 sql
|
||||
*/
|
||||
private EncryptQueryWrapper(T entity, Class<T> entityClass, AtomicInteger paramNameSeq,
|
||||
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
|
||||
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
|
||||
super.setEntity(entity);
|
||||
super.setEntityClass(entityClass);
|
||||
this.paramNameSeq = paramNameSeq;
|
||||
this.paramNameValuePairs = paramNameValuePairs;
|
||||
this.expression = mergeSegments;
|
||||
this.paramAlias = paramAlias;
|
||||
this.lastSql = lastSql;
|
||||
this.sqlComment = sqlComment;
|
||||
this.sqlFirst = sqlFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个支持 lambda 函数写法的加密 wrapper
|
||||
*/
|
||||
@Override
|
||||
public EncryptLambdaQueryWrapper<T> lambda() {
|
||||
return new EncryptLambdaQueryWrapper<>(getEntity(), getEntityClass(), sqlSelect, paramNameSeq, paramNameValuePairs,
|
||||
expression, paramAlias, lastSql, sqlComment, sqlFirst);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于生成嵌套 sql
|
||||
* <p>
|
||||
* 故 sqlSelect 不向下传递
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
protected EncryptQueryWrapper<T> instance() {
|
||||
return new EncryptQueryWrapper<>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs, new MergeSegments(),
|
||||
paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2025, baomidou (jobob@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springblade.core.mp.encrypt.wrapper;
|
||||
|
||||
/**
|
||||
* EncryptWrappers
|
||||
*
|
||||
* @author hubin miemie HCL BladeX
|
||||
*/
|
||||
public final class EncryptWrappers {
|
||||
|
||||
/**
|
||||
* 获取 QueryWrapper<T>
|
||||
*
|
||||
* @param <T> 实体类泛型
|
||||
* @return QueryWrapper<T>
|
||||
*/
|
||||
public static <T> EncryptQueryWrapper<T> query() {
|
||||
return new EncryptQueryWrapper<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 QueryWrapper<T>
|
||||
*
|
||||
* @param entity 实体类
|
||||
* @param <T> 实体类泛型
|
||||
* @return QueryWrapper<T>
|
||||
*/
|
||||
public static <T> EncryptQueryWrapper<T> query(T entity) {
|
||||
return new EncryptQueryWrapper<>(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 QueryWrapper<T>
|
||||
*
|
||||
* @param entityClass 实体类class
|
||||
* @param <T> 实体类泛型
|
||||
* @return QueryWrapper<T>
|
||||
*/
|
||||
public static <T> EncryptQueryWrapper<T> query(Class<T> entityClass) {
|
||||
return new EncryptQueryWrapper<>(entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 LambdaQueryWrapper<T>
|
||||
*
|
||||
* @return LambdaQueryWrapper<T>
|
||||
*/
|
||||
public static <T> EncryptLambdaQueryWrapper<T> lambdaQuery() {
|
||||
return new EncryptLambdaQueryWrapper<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 LambdaQueryWrapper<T>
|
||||
*
|
||||
* @param entity 实体类
|
||||
* @param <T> 实体类泛型
|
||||
* @return LambdaQueryWrapper<T>
|
||||
*/
|
||||
public static <T> EncryptLambdaQueryWrapper<T> lambdaQuery(T entity) {
|
||||
return new EncryptLambdaQueryWrapper<>(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 LambdaQueryWrapper<T>
|
||||
*
|
||||
* @param entityClass 实体类class
|
||||
* @param <T> 实体类泛型
|
||||
* @return LambdaQueryWrapper<T>
|
||||
*/
|
||||
public static <T> EncryptLambdaQueryWrapper<T> lambdaQuery(Class<T> entityClass) {
|
||||
return new EncryptLambdaQueryWrapper<>(entityClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
blade:
|
||||
mybatis-plus:
|
||||
encrypt:
|
||||
# 是否启用加密功能
|
||||
enabled: true
|
||||
# 滑动窗口大小,用于加密字段模糊搜索
|
||||
window-size: 3
|
||||
# 加密算法类型
|
||||
algorithm: sm4
|
||||
# 加密算法密钥,必须配置
|
||||
secret-key: ${BLADE_MYBATIS_PLUS_ENCRYPT_SECRET_KEY:}
|
||||
Reference in New Issue
Block a user