前言
目前参加工作也有1年多了,收获颇丰,打算在这里分享一下日常使用的工具类,也方便大家学习工作里进行使用。
一、Hutool
Hutool是一个Java工具类库,它封装了很多常用的Java工具类,如加密解密、文件操作、日期时间处理、Http客户端等。它的目标是让Java开发变得更加简单、高效。日常开发必备工具包!!
二、Redis缓存的工具类
代码如下(示例):
/**
* @author wlm
* @date 2023-03-02
*/
@Component
public class RedisCache
{
@Autowired
public RedisTemplate redisTemplate;
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value);
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
{
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout)
{
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit)
{
return redisTemplate.expire(key, timeout, unit);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key)
{
return redisTemplate.delete(key);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection)
{
return redisTemplate.delete(collection);
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList)
{
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key)
{
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
{
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
}
/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key)
{
return redisTemplate.opsForSet().members(key);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
{
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key)
{
return redisTemplate.opsForHash().entries(key);
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
{
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey)
{
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
/**
* 删除Hash中的数据
*
* @param key
* @param hkey
*/
public void delCacheMapValue(final String key, final String hkey)
{
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hkey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
{
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern)
{
return redisTemplate.keys(pattern);
}
}
三、PageUtils
日常开发里经常会遇到一些自定义对象并不能使用MabatisPlus的框架的Page方法,这个PageUtils可以将任意的list的转换成一个Page对象,即拿即用。
代码如下:
public class PageUtils {
private static final ThreadLocal<Page<?>> LOCAL_PAGE = new ThreadLocal<>();
public static void startPage(int pageNum, int pageSize) {
Page<?> page = new Page<>(pageNum, pageSize);
LOCAL_PAGE.set(page);
}
public static <T> Page<T> endPage() {
Page<T> page = (Page<T>) LOCAL_PAGE.get();
LOCAL_PAGE.remove();
return page;
}
public static <T> IPage<T> getPage(int pageNum, int pageSize, List<T> t) {
// 创建IPage对象,指定当前页码和每页显示的记录数
IPage<T> page = new Page<>(pageNum, pageSize);
// 设置总记录数
page.setTotal(t.size());
// 计算分页起始索引
int startIndex = (pageNum - 1) * pageSize;
// 计算分页结束索引
int endIndex = Math.min(startIndex + pageSize, t.size());
// 截取分页数据
List<T> pageData = t.subList(startIndex, endIndex);
// 设置分页数据
page.setRecords(pageData);
// 返回分页后的数据
return page;
}
}
四、AES加密工具类
最近由于公司项目有三级等保要求,给我的任务是将数据库里所有的数据进行aes加密,入库加密,出库解密,返回给前端时进行脱敏,这一块当时确实难到我了,普通的加解密不难,但由于这个还需要用sql也能解密,方便业务的模糊查询工作,导致开发起来确实有一些难度,但也解决掉了。完成的一套是工具类+加密注解+脱敏注解进行完美解决,有小伙伴需要具体细节的话可以点赞私聊我。
代码如下:
public class MPAESUtil {
/**
* 密钥长度: 128, 192 or 256
*/
private static final int KEY_SIZE = 256;
/**
* 加密/解密算法名称
*/
private static final String ALGORITHM = "AES";
/**
* 随机数生成器(RNG)算法名称
*/
private static final String RNG_ALGORITHM = "SHA1PRNG";
/**
* 生成密钥的种子不可泄露
*/
public static final String KEY = "你自己的密钥";
/**
* 生成密钥对象
*/
private static SecretKey generateKey(byte[] key) throws Exception {
byte[] bytes = KEY.getBytes();
// 创建安全随机数生成器
SecureRandom random = SecureRandom.getInstance(RNG_ALGORITHM);
// 设置 密钥key的字节数组 作为安全随机数生成器的种子
random.setSeed(key);
// 创建 AES算法生成器
KeyGenerator gen = KeyGenerator.getInstance(ALGORITHM);
// 初始化算法生成器
gen.init(KEY_SIZE, random);
// 生成 AES密钥对象, 也可以直接创建密钥对象: return new SecretKeySpec(key, ALGORITHM);
// return gen.generateKey();
return new SecretKeySpec(key, ALGORITHM);
}
/**
* 数据加密: 明文 -> 密文
*/
public static String encrypt(String content, byte[] key) {
// 生成密钥对象
SecretKey secKey;
try {
secKey = generateKey(key);
// 获取 AES 密码器
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 初始化密码器(加密模型)
cipher.init(Cipher.ENCRYPT_MODE, secKey);
// 加密数据, 返回密文
byte[] plainBytes = content.getBytes("utf-8");
byte[] result = cipher.doFinal(plainBytes);
return Base64.getEncoder().encodeToString(result);//通过Base64转码返回
} catch (Exception e) {
throw new RuntimeException( "AES 加密失败:" + e.getMessage());
}
}
/**
* 数据解密: 密文 -> 明文
*/
public static String decrypt(String content, byte[] key) {
try {
// 生成密钥对象
SecretKey secKey = generateKey(key);
// 获取 AES 密码器
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 初始化密码器(解密模型)
cipher.init(Cipher.DECRYPT_MODE, secKey);
// 解密数据, 返回明文
byte[] result = cipher.doFinal(Base64.getDecoder().decode(content));
return new String(result, "utf-8");
} catch (Exception e) {
throw new RuntimeException( "AES 解密失败:" + e.getMessage());
}
}
/**
* 加密文件: 明文输入 -> 密文输出
*/
public static void encryptFile(File plainIn, File cipherOut, byte[] key) throws Exception {
aesFile(plainIn, cipherOut, key, true);
}
/**
* 解密文件: 密文输入 -> 明文输出
*/
public static void decryptFile(File cipherIn, File plainOut, byte[] key) throws Exception {
aesFile(plainOut, cipherIn, key, false);
}
/**
* AES 加密/解密文件
*/
private static void aesFile(File plainFile, File cipherFile, byte[] key, boolean isEncrypt) throws Exception {
// 获取 AES 密码器
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 生成密钥对象
SecretKey secKey = generateKey(key);
// 初始化密码器
cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secKey);
// 加密/解密数据
InputStream in = null;
OutputStream out = null;
try {
if (isEncrypt) {
// 加密: 明文文件为输入, 密文文件为输出
in = new FileInputStream(plainFile);
out = new FileOutputStream(cipherFile);
} else {
// 解密: 密文文件为输入, 明文文件为输出
in = new FileInputStream(cipherFile);
out = new FileOutputStream(plainFile);
}
byte[] buf = new byte[1024];
int len = -1;
// 循环读取数据 加密/解密
while ((len = in.read(buf)) != -1) {
out.write(cipher.update(buf, 0, len));
}
out.write(cipher.doFinal()); // 最后需要收尾
out.flush();
} finally {
close(in);
close(out);
}
}
private static void close(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException e) {
// nothing
}
}
}
public static void main(String[] args) {
String encrypt = MPAESUtil.encrypt("18155907185", MPAESUtil.KEY.getBytes());
System.out.println(encrypt);
}
}
2024-04-14
今天来补充下sql里面的解析方法,形成sql和代码的加解密互通。
假设我上面的key=‘1111111111111111’(这里注意key必须是16位),机密内容为123
代码加密过程:
public static void main(String[] args) {
String encrypt = MPAESUtil.encrypt("123","1111111111111111".getBytes());
System.out.println("加密结果:"+encrypt);
String decrypt = MPAESUtil.decrypt(encrypt, "1111111111111111".getBytes());
System.out.println("解密结果:"+decrypt);
}
结果:
Sql加密过程:
加密sql
SELECT to_base64(AES_ENCRYPT('123','1111111111111111'))
解密sql:
SELECT AES_DECRYPT( from_base64 ('XtRDz3Z8+6VwWb4WUIqGBQ=='), '1111111111111111')
发现和上面的代码一致,这里就完成了代码与sql的加解密互通工作。但往后面如果有查询的话应该怎么办呢,之前是明文可以模糊查询,现在加密之后,存在数据库里都是密文了,无法直接进行模糊查询。结局方案是在sql里面就进行解密查询,示例代码如下:
<if test="ro.phone != null and ro.phone != ''">
AND AES_DECRYPT( from_base64 (a.phone), '1111111111111111') like concat('%',#{ro.phone},'%')
</if>
就可以继续进行模糊查询啦。
总结
在开发过程中,各种工具必不可少,可以大大简化工作流程,提高工作效率(摸鱼效率),有小伙伴有不明白的地方,或者愿意互相分享的也可以私信我哦