feat(utils): 添加 AES 加密解密工具类
All checks were successful
Gitea Actions Build / Build (push) Successful in 51s

- 新增 AesUtils 类,提供 AES/GCM 模式的加密解密方法- 更新 .prettierrc配置,将 useTabs 设置为 true
- 重构 Base64Utils 和 Collection 类的代码结构- 简化 ComponentBean 注解定义
This commit is contained in:
Armamem0t 2025-08-17 19:12:08 +08:00
parent ceef51506d
commit 34dccd1895
Signed by: minglipro
GPG Key ID: 5F355A77B22AA93B
48 changed files with 4214 additions and 3952 deletions

View File

@ -4,5 +4,5 @@
"prettier-plugin-java"
],
"tabWidth": 4,
"useTabs": false
"useTabs": true
}

View File

@ -1,4 +1,4 @@
JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils
VERSIONS=1.0.5
VERSIONS=1.0.7

View File

@ -4,7 +4,6 @@ import com.mingliqiye.utils.collection.Lists;
import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
import java.util.List;
public class Main {
@ -12,7 +11,13 @@ public class Main {
public static void main(String[] args) {
// 字符串工具使用示例
System.out.println("=== 字符串工具使用示例 ===");
String formatted = StringUtil.format("你好 {},今天是{}年{}月{}日", "张三", 2025, 7, 25);
String formatted = StringUtil.format(
"你好 {},今天是{}年{}月{}日",
"张三",
2025,
7,
25
);
System.out.println(formatted);
List<String> fruits = Lists.newArrayList("苹果", "香蕉", "橙子");
@ -23,17 +28,25 @@ public class Main {
System.out.println("\n=== 时间工具使用示例 ===");
DateTime now = DateTime.now();
System.out.println("当前时间: " + now);
System.out.println("标准格式: " + now.format(Formatter.STANDARD_DATETIME));
System.out.println(
"标准格式: " + now.format(Formatter.STANDARD_DATETIME)
);
DateTime specificDate = DateTime.of(2025, 1, 1, 12, 0, 0);
System.out.println("指定时间: " + specificDate.format(Formatter.STANDARD_DATETIME));
System.out.println(
"指定时间: " + specificDate.format(Formatter.STANDARD_DATETIME)
);
// 集合工具使用示例
System.out.println("\n=== 集合工具使用示例 ===");
List<Integer> numbers = Lists.newArrayList(10, 20, 30);
System.out.println("数字列表: " + numbers);
List<String> linkedList = Lists.newLinkedList("第一个", "第二个", "第三个");
List<String> linkedList = Lists.newLinkedList(
"第一个",
"第二个",
"第三个"
);
System.out.println("链表内容: " + linkedList);
}
}

View File

@ -0,0 +1,112 @@
package com.mingliqiye.utils.aes;
import com.mingliqiye.utils.base64.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.SecureRandom;
public class AesUtils {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final int GCM_IV_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 16;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* AES加密方法使用GCM模式
* @param sSrc 待加密的字符串
* @param sKey 加密密钥
* @return 加密后的字符串格式为 IV:EncryptedData+Tag均为Base64编码
* @throws GeneralSecurityException 加密错误
*/
public static String encrypt(String sSrc, String sKey)
throws GeneralSecurityException {
if (sKey == null) {
return null;
}
// 生成密钥
SecretKeySpec secretKeySpec = createSecretKey(sKey);
// 生成安全随机IV
byte[] iv = new byte[GCM_IV_LENGTH];
SECURE_RANDOM.nextBytes(iv);
// 初始化加密器
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(
GCM_TAG_LENGTH * 8,
iv
);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
// 执行加密
byte[] encrypted = cipher.doFinal(
sSrc.getBytes(StandardCharsets.UTF_8)
);
// 将IV和加密数据包含认证标签组合返回
return Base64Utils.encode(
(Base64Utils.encode(iv) +
":" +
Base64Utils.encode(encrypted)).getBytes()
);
}
/**
* AES解密方法使用GCM模式
* @param sSrc 待解密的字符串格式为 IV:EncryptedData+Tag均为Base64编码
* @param sKey 解密密钥
* @return 解密后的原始字符串
*/
public static String decrypt(String sSrc, String sKey) {
if (sKey == null) {
return null;
}
try {
// 分割IV和加密数据
String sSrcs = new String(Base64Utils.decode(sSrc));
String[] parts = sSrcs.split(":", 2);
if (parts.length != 2) {
return null;
}
byte[] iv = Base64Utils.decode(parts[0]);
byte[] encryptedData = Base64Utils.decode(parts[1]);
if (iv.length != GCM_IV_LENGTH) {
return null;
}
SecretKeySpec secretKeySpec = createSecretKey(sKey);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(
GCM_TAG_LENGTH * 8,
iv
);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
byte[] original = cipher.doFinal(encryptedData);
return new String(original, StandardCharsets.UTF_8);
} catch (Exception e) {
return null;
}
}
/**
* 创建AES密钥支持任意长度的密钥
* @param sKey 字符串密钥
* @return SecretKeySpec对象
* @throws Exception 可能抛出的异常
*/
private static SecretKeySpec createSecretKey(String sKey)
throws GeneralSecurityException {
byte[] key = sKey.getBytes(StandardCharsets.UTF_8);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(key);
return new SecretKeySpec(digest, ALGORITHM);
}
}

View File

@ -2,7 +2,6 @@ package com.mingliqiye.utils.bean;
import com.mingliqiye.utils.bean.annotation.ComponentBean;
import com.mingliqiye.utils.bean.annotation.InjectBean;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;

View File

@ -70,9 +70,6 @@ public class SpringBeanUtil implements ApplicationContextAware {
public void setApplicationContext(
@NotNull ApplicationContext applicationContext
) throws BeansException {
// 避免重复赋值确保只设置一次ApplicationContext
if (SpringBeanUtil.applicationContext == null) {
SpringBeanUtil.applicationContext = applicationContext;
}
}
}

View File

@ -1,13 +1,12 @@
package com.mingliqiye.utils.collection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* 集合工具类提供对列表和数组的常用操作方法

View File

@ -1,8 +1,7 @@
package com.mingliqiye.utils.collection;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import org.jetbrains.annotations.NotNull;
/**
* Lists工具类提供了一系列创建List实现的便捷方法

View File

@ -1,9 +1,6 @@
package com.mingliqiye.utils.file;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import lombok.Setter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -15,6 +12,8 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
/**
* 文件工具类提供常用的文件操作方法

View File

@ -1,14 +1,13 @@
package com.mingliqiye.utils.hash;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.mindrot.jbcrypt.BCrypt;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.mindrot.jbcrypt.BCrypt;
/**
* 提供常用的哈希计算工具方法包括文件哈希值计算BCrypt 加密等

View File

@ -2,7 +2,6 @@ package com.mingliqiye.utils.minecraft.slp;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mingliqiye.utils.network.NetworkEndpoint;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;

View File

@ -1,13 +1,12 @@
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Pattern;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
/**
* 网络地址类用于表示一个网络地址IP或域名并提供相关操作

View File

@ -1,10 +1,9 @@
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import java.io.Serializable;
import java.net.InetSocketAddress;
import lombok.Getter;
/**
* IP和端口聚集类用于封装网络地址与端口信息

View File

@ -1,9 +1,8 @@
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import java.io.Serializable;
import lombok.Getter;
/**
* 网络端口类

View File

@ -1,7 +1,6 @@
package com.mingliqiye.utils.string;
import com.mingliqiye.utils.collection.Lists;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;

View File

@ -1,7 +1,6 @@
package com.mingliqiye.utils.system;
import com.mingliqiye.utils.collection.Lists;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;

View File

@ -1,14 +1,13 @@
package com.mingliqiye.utils.time;
import lombok.Getter;
import lombok.Setter;
import lombok.var;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import lombok.Getter;
import lombok.Setter;
import lombok.var;
/**
* 时间工具类用于处理日期时间的转换格式化等操作

View File

@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
import java.io.IOException;
/**

View File

@ -2,13 +2,12 @@ package com.mingliqiye.utils.time.typehandlers;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
import java.sql.*;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.*;
/**
* DateTime类型处理器类
* 用于在MyBatis中处理DateTime类型与数据库VARCHAR类型之间的转换

View File

@ -0,0 +1,66 @@
package com.mingliqiye.utils.uuid;
import lombok.var;
/**
* MySQL UUID格式与标准UUID格式相互转换工具类
* <p>
* MySQL使用不同的字节顺序存储UUID此类提供了在MySQL格式和标准UUID格式之间转换的方法
* @author MingLiPro
*/
public class MysqlUUIDv1 {
/**
* 将MySQL格式的UUID转换为标准UUID格式
*
* @param uuid MySQL格式的UUID字节数组长度必须为16字节
* @return 标准UUID格式的字节数组长度为16字节
*/
public static byte[] mysqlToUuid(byte[] uuid) {
var reuuid = new byte[16];
// 转换时间戳低位部分
reuuid[4] = uuid[0];
reuuid[5] = uuid[1];
reuuid[6] = uuid[2];
reuuid[7] = uuid[3];
// 转换时间戳中位部分
reuuid[2] = uuid[4];
reuuid[3] = uuid[5];
// 转换时间戳高位部分
reuuid[0] = uuid[6];
reuuid[1] = uuid[7];
// 复制时钟序列和节点标识部分
System.arraycopy(uuid, 8, reuuid, 8, 8);
return reuuid;
}
/**
* 将标准UUID格式转换为MySQL格式的UUID
*
* @param uuid 标准UUID格式的字节数组长度必须为16字节
* @return MySQL格式的UUID字节数组长度为16字节
*/
public static byte[] uuidToMysql(byte[] uuid) {
var reuuid = new byte[16];
// 转换时间戳高位部分
reuuid[6] = uuid[0];
reuuid[7] = uuid[1];
// 转换时间戳中位部分
reuuid[4] = uuid[2];
reuuid[5] = uuid[3];
// 转换时间戳低位部分
reuuid[0] = uuid[4];
reuuid[1] = uuid[5];
reuuid[2] = uuid[6];
reuuid[3] = uuid[7];
// 复制时钟序列和节点标识部分
System.arraycopy(uuid, 8, reuuid, 8, 8);
return reuuid;
}
}

View File

@ -4,8 +4,7 @@ import com.github.f4b6a3.uuid.UuidCreator;
import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.DateTimeOffset;
import com.mingliqiye.utils.time.Formatter;
import lombok.Setter;
import lombok.Data;
import java.io.Serializable;
import java.nio.ByteBuffer;
@ -19,7 +18,7 @@ import java.util.Objects;
*
* @author MingLiPro
*/
@Setter
@Data
public class UUID implements Serializable {
/**
@ -69,6 +68,9 @@ public class UUID implements Serializable {
* @return 新建的 UUID 实例
*/
public static UUID of(byte[] bytes) {
if (bytes == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
long msb = bb.getLong();
long lsb = bb.getLong();
@ -82,7 +84,10 @@ public class UUID implements Serializable {
* @return 解析后的 UUID 实例
* @throws UUIDException 如果解析失败
*/
public static UUID ofString(String data) {
public static UUID of(String data) {
if (data == null) {
return null;
}
try {
java.util.UUID uuid1 = java.util.UUID.fromString(data);
UUID uuid = new UUID();
@ -99,6 +104,9 @@ public class UUID implements Serializable {
* @return 表示该 UUID 的字节数组
*/
public byte[] toBytes() {
if (this.uuid == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
@ -178,15 +186,6 @@ public class UUID implements Serializable {
if (uuid == null) {
return "UUID(null)";
}
if (uuid.version() == 1) {
return StringUtil.format(
"UUID(uuid={},time={},mac={},version={})",
toUUIDString(true),
getDateTime().format(Formatter.STANDARD_DATETIME),
extractMACFromUUID(),
uuid.version()
);
}
return StringUtil.format(
"UUID(uuid={},version={})",
toUUIDString(true),
@ -203,6 +202,9 @@ public class UUID implements Serializable {
if (uuid == null) {
return null;
}
if (uuid.version() != 1) {
return null;
}
return DateTime.of(uuid.timestamp() / 10_000).add(
DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
);

View File

@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.mingliqiye.utils.uuid.UUID;
import com.mingliqiye.utils.uuid.UUIDException;
import java.io.IOException;
/**

View File

@ -6,7 +6,6 @@ import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.nio.ByteBuffer;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -22,32 +21,6 @@ import java.sql.SQLException;
@MappedJdbcTypes(JdbcType.BINARY)
public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
/**
* UUID 对象转换为二进制字节数组
*
* @param uuid 要转换的 UUID 对象
* @return 包含 UUID 数据的 16 字节二进制数组
*/
public static byte[] UUID_TO_BIN(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.GetUUID().getMostSignificantBits());
bb.putLong(uuid.GetUUID().getLeastSignificantBits());
return bb.array();
}
/**
* 将二进制字节数组转换为 UUID 对象
*
* @param bytes 包含 UUID 数据的二进制字节数组
* @return 转换后的 UUID 对象如果输入为 null 则返回 null
*/
public static UUID BIN_TO_UUID(byte[] bytes) {
if (bytes == null) {
return null;
}
return UUID.of(bytes);
}
/**
* 设置非空参数到 PreparedStatement
*
@ -64,7 +37,7 @@ public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setBytes(i, UUID_TO_BIN(parameter));
ps.setBytes(i, UUIDConverter.UUID_TO_BIN(parameter));
}
/**
@ -78,7 +51,7 @@ public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
@Override
public UUID getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnName));
return UUIDConverter.BIN_TO_UUID(rs.getBytes(columnName));
}
/**
@ -92,7 +65,7 @@ public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnIndex));
return UUIDConverter.BIN_TO_UUID(rs.getBytes(columnIndex));
}
/**
@ -106,6 +79,6 @@ public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return BIN_TO_UUID(cs.getBytes(columnIndex));
return UUIDConverter.BIN_TO_UUID(cs.getBytes(columnIndex));
}
}

View File

@ -0,0 +1,31 @@
package com.mingliqiye.utils.uuid.typehandlers;
import com.mingliqiye.utils.uuid.MysqlUUIDv1;
import com.mingliqiye.utils.uuid.UUID;
public class UUIDConverter {
public static String UUID_TO_STR(UUID uuid) {
return uuid.toUUIDString();
}
public static UUID STR_TO_UUID(String string) {
return UUID.of(string);
}
public static byte[] UUID_TO_BIN(UUID uuid) {
return uuid.toBytes();
}
public static UUID BIN_TO_UUID(byte[] bytes) {
return UUID.of(bytes);
}
public static byte[] MYSQL_UUID_TO_BIN(UUID uuid) {
return MysqlUUIDv1.uuidToMysql(uuid.toBytes());
}
public static UUID BIN_TO_MYSQL_UUID(byte[] bytes) {
return UUID.of(MysqlUUIDv1.mysqlToUuid(bytes));
}
}

View File

@ -0,0 +1,83 @@
package com.mingliqiye.utils.uuid.typehandlers;
import com.mingliqiye.utils.uuid.UUID;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
/**
* UUIDBinaryTypeHandler 类用于处理 UUID 类型与数据库 BINARY 类型之间的转换
* 该类继承自 BaseTypeHandler专门处理 UUID 对象的序列化和反序列化
*
* @author MingLiPro
*/
@MappedTypes({ UUID.class })
@MappedJdbcTypes(JdbcType.VARCHAR)
public class UUIDStringTypeHandler extends BaseTypeHandler<UUID> {
/**
* 设置非空参数到 PreparedStatement
*
* @param ps PreparedStatement 对象
* @param i 参数在 SQL 语句中的位置索引
* @param parameter 要设置的 UUID 参数值
* @param jdbcType JDBC 类型信息
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setString(i, UUIDConverter.UUID_TO_STR(parameter));
}
/**
* ResultSet 中根据列名获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnName 数据库列名
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return UUIDConverter.STR_TO_UUID(rs.getString(columnName));
}
/**
* ResultSet 中根据列索引获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnIndex 数据库列索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return UUIDConverter.STR_TO_UUID(rs.getString(columnIndex));
}
/**
* CallableStatement 中根据参数索引获取可为空的 UUID 结果
*
* @param cs CallableStatement 对象
* @param columnIndex 参数索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return UUIDConverter.STR_TO_UUID(cs.getString(columnIndex));
}
}