diff --git a/CHANGELOG.md b/CHANGELOG.md index 872cd4e56..1c1df9dd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.17 (2021-11-16) +# 5.7.17 (2021-11-19) ### 🐣新特性 * 【core 】 增加AsyncUtil(pr#457@Gitee) @@ -17,9 +17,13 @@ * 【core 】 改进TextFinder,支持限制结束位置及反向查找模式 * 【core 】 Opt增加部分方法(pr#459@Gitee) * 【core 】 增加DefaultCloneable(pr#459@Gitee) +* 【core 】 CollStreamUtil增加是否并行的重载(pr#467@Gitee) +* 【core 】 ResourceClassLoader增加缓存(pr#1959@Github) +* 【crypto 】 增加CipherWrapper,增加setRandom(issue#1958@Github) * ### 🐞Bug修复 * 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github) +* 【cache 】 修复WeakCache键值强关联导致的无法回收问题(issue#1953@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java index f9dca42a0..dc203cb9c 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java @@ -17,11 +17,11 @@ public class CacheObj implements Serializable{ protected final V obj; /** 上次访问时间 */ - private volatile long lastAccess; + protected volatile long lastAccess; /** 访问次数 */ protected AtomicLong accessCount = new AtomicLong(); /** 对象存活时长,0表示永久存活*/ - private final long ttl; + protected final long ttl; /** * 构造 diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java index 3a98bc601..21b2bb16a 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java @@ -1,5 +1,10 @@ package cn.hutool.cache.impl; +import cn.hutool.cache.Cache; +import cn.hutool.core.lang.func.Func0; +import cn.hutool.core.lang.mutable.MutableObj; + +import java.util.Iterator; import java.util.WeakHashMap; /** @@ -7,18 +12,112 @@ import java.util.WeakHashMap; * 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。
* 丢弃某个键时,其条目从映射中有效地移除。
* + * @param 键类型 + * @param 值类型 * @author Looly - * - * @param 键 - * @param 值 - * @author looly * @since 3.0.7 */ -public class WeakCache extends TimedCache{ +public class WeakCache implements Cache { private static final long serialVersionUID = 1L; + TimedCache, V> timedCache; + + /** + * 构造 + * + * @param timeout 超时 + */ public WeakCache(long timeout) { - super(timeout, new WeakHashMap<>()); + this.timedCache = new TimedCache<>(timeout, new WeakHashMap<>()); } + @Override + public int capacity() { + return timedCache.capacity(); + } + + @Override + public long timeout() { + return timedCache.timeout(); + } + + @Override + public void put(K key, V object) { + timedCache.put(new MutableObj<>(key), object); + } + + @Override + public void put(K key, V object, long timeout) { + timedCache.put(new MutableObj<>(key), object, timeout); + } + + @Override + public V get(K key, boolean isUpdateLastAccess, Func0 supplier) { + return timedCache.get(new MutableObj<>(key), isUpdateLastAccess, supplier); + } + + @Override + public V get(K key, boolean isUpdateLastAccess) { + return timedCache.get(new MutableObj<>(key), isUpdateLastAccess); + } + + @Override + public Iterator> cacheObjIterator() { + final Iterator, V>> timedIter = timedCache.cacheObjIterator(); + return new Iterator>() { + @Override + public boolean hasNext() { + return timedIter.hasNext(); + } + + @Override + public CacheObj next() { + final CacheObj, V> next = timedIter.next(); + final CacheObj nextNew = new CacheObj<>(next.key.get(), next.obj, next.ttl); + nextNew.lastAccess = next.lastAccess; + nextNew.accessCount = next.accessCount; + return nextNew; + } + }; + } + + @Override + public int prune() { + return timedCache.prune(); + } + + @Override + public boolean isFull() { + return timedCache.isFull(); + } + + @Override + public void remove(K key) { + timedCache.remove(new MutableObj<>(key)); + } + + @Override + public void clear() { + timedCache.clear(); + } + + @Override + public int size() { + return timedCache.size(); + } + + @Override + public boolean isEmpty() { + return timedCache.isEmpty(); + } + + @Override + public boolean containsKey(K key) { + return timedCache.containsKey(new MutableObj<>(key)); + } + + @Override + public Iterator iterator() { + return timedCache.iterator(); + } } diff --git a/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java b/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java new file mode 100644 index 000000000..ce7b03dd8 --- /dev/null +++ b/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java @@ -0,0 +1,21 @@ +package cn.hutool.cache; + +import cn.hutool.cache.impl.WeakCache; +import org.junit.Assert; +import org.junit.Test; + +public class WeakCacheTest { + + @Test + public void removeTest(){ + final WeakCache cache = new WeakCache<>(-1); + cache.put("abc", "123"); + cache.put("def", "456"); + + Assert.assertEquals(2, cache.size()); + + cache.remove("abc"); + + Assert.assertEquals(1, cache.size()); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java index 53243cb71..693be7936 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java @@ -2,6 +2,7 @@ package cn.hutool.core.collection; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.stream.StreamUtil; import java.util.*; import java.util.function.BiFunction; @@ -27,10 +28,27 @@ public class CollStreamUtil { * @return 转化后的map */ public static Map toIdentityMap(Collection collection, Function key) { + return toIdentityMap(collection, key, false); + } + + + /** + * 将collection转化为类型不变的map
+ * {@code Collection ----> Map} + * + * @param collection 需要转化的集合 + * @param key V类型转化为K类型的lambda方法 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param map中的key类型 + * @return 转化后的map + */ + public static Map toIdentityMap(Collection collection, Function key, boolean isParallel) { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + return StreamUtil.of(collection, isParallel) + .collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); } /** @@ -46,12 +64,27 @@ public class CollStreamUtil { * @return 转化后的map */ public static Map toMap(Collection collection, Function key, Function value) { + return toMap(collection, key, value, false); + } + + /** + * @param collection 需要转化的集合 + * @param key E类型转化为K类型的lambda方法 + * @param value E类型转化为V类型的lambda方法 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param map中的key类型 + * @param map中的value类型 + * @return 转化后的map + */ + public static Map toMap(Collection collection, Function key, Function value, boolean isParallel) { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l)); + return StreamUtil.of(collection, isParallel).collect(Collectors.toMap(key, value, (l, r) -> l)); } + /** * 将collection按照规则(比如有相同的班级id)分类成map
* {@code Collection -------> Map> } @@ -63,12 +96,25 @@ public class CollStreamUtil { * @return 分类后的map */ public static Map> groupByKey(Collection collection, Function key) { + return groupByKey(collection, key, false); + } + + /** + * 将collection按照规则(比如有相同的班级id)分类成map
+ * {@code Collection -------> Map> } + * + * @param collection 需要分类的集合 + * @param key 分类的规则 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param map中的key类型 + * @return 分类后的map + */ + public static Map> groupByKey(Collection collection, Function key, boolean isParallel) { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection - .stream() - .collect(Collectors.groupingBy(key, Collectors.toList())); + return StreamUtil.of(collection, isParallel).collect(Collectors.groupingBy(key, Collectors.toList())); } /** @@ -84,11 +130,29 @@ public class CollStreamUtil { * @return 分类后的map */ public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) { + return groupBy2Key(collection, key1, key2, false); + } + + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map>> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param isParallel 是否并行流 + * @param 集合元素类型 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @return 分类后的map + */ + public static Map>> groupBy2Key(Collection collection, Function key1, + Function key2, boolean isParallel) { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection - .stream() + return StreamUtil.of(collection, isParallel) .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList()))); } @@ -105,11 +169,28 @@ public class CollStreamUtil { * @return 分类后的map */ public static Map> group2Map(Collection collection, Function key1, Function key2) { + return group2Map(collection, key1, key2, false); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param isParallel 是否并行流 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @param collection中的泛型 + * @return 分类后的map + */ + public static Map> group2Map(Collection collection, + Function key1, Function key2, boolean isParallel) { if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { return Collections.emptyMap(); } - return collection - .stream() + return StreamUtil.of(collection, isParallel) .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); } @@ -124,11 +205,25 @@ public class CollStreamUtil { * @return 转化后的list */ public static List toList(Collection collection, Function function) { + return toList(collection, function, false); + } + + /** + * 将collection转化为List集合,但是两者的泛型不同
+ * {@code Collection ------> List } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为list泛型的lambda表达式 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param List中的泛型 + * @return 转化后的list + */ + public static List toList(Collection collection, Function function, boolean isParallel) { if (CollUtil.isEmpty(collection)) { return Collections.emptyList(); } - return collection - .stream() + return StreamUtil.of(collection, isParallel) .map(function) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -145,11 +240,25 @@ public class CollStreamUtil { * @return 转化后的Set */ public static Set toSet(Collection collection, Function function) { - if (CollUtil.isEmpty(collection) || function == null) { + return toSet(collection, function, false); + } + + /** + * 将collection转化为Set集合,但是两者的泛型不同
+ * {@code Collection ------> Set } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为set泛型的lambda表达式 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param Set中的泛型 + * @return 转化后的Set + */ + public static Set toSet(Collection collection, Function function, boolean isParallel) { + if (CollUtil.isEmpty(collection)) { return Collections.emptySet(); } - return collection - .stream() + return StreamUtil.of(collection, isParallel) .map(function) .filter(Objects::nonNull) .collect(Collectors.toSet()); diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java index c7e11d7b7..383f6b98b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java @@ -12,12 +12,16 @@ import java.util.Map; * 资源类加载器,可以加载任意类型的资源类 * * @param {@link Resource}接口实现类 - * @author looly + * @author looly, lzpeng * @since 5.5.2 */ public class ResourceClassLoader extends SecureClassLoader { private final Map resourceMap; + /** + * 缓存已经加载的类 + */ + private final Map> cacheClassMap; /** * 构造 @@ -28,25 +32,42 @@ public class ResourceClassLoader extends SecureClassLoader { public ResourceClassLoader(ClassLoader parentClassLoader, Map resourceMap) { super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader())); this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>()); + this.cacheClassMap = new HashMap<>(); } /** * 增加需要加载的类资源 + * * @param resource 资源,可以是文件、流或者字符串 * @return this */ - public ResourceClassLoader addResource(T resource){ + public ResourceClassLoader addResource(T resource) { this.resourceMap.put(resource.getName(), resource); return this; } @Override protected Class findClass(String name) throws ClassNotFoundException { + final Class clazz = cacheClassMap.computeIfAbsent(name, this::defineByName); + if (clazz == null) { + return super.findClass(name); + } + return clazz; + } + + /** + * 从给定资源中读取class的二进制流,然后生成类
+ * 如果这个类资源不存在,返回{@code null} + * + * @param name 类名 + * @return 定义的类 + */ + private Class defineByName(String name) { final Resource resource = resourceMap.get(name); if (null != resource) { final byte[] bytes = resource.readBytes(); return defineClass(name, bytes, 0, bytes.length); } - return super.findClass(name); + return null; } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java b/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java new file mode 100644 index 000000000..bb9a2578a --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java @@ -0,0 +1,121 @@ +package cn.hutool.crypto; + +import javax.crypto.Cipher; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * {@link Cipher}包装类,提供初始化模式等额外方法
+ * 包装之后可提供自定义或默认的: + *
    + *
  • {@link AlgorithmParameterSpec}
  • + *
  • {@link SecureRandom}
  • + *
+ * + * @author looly + * @since 5.7.17 + */ +public class CipherWrapper { + + private final Cipher cipher; + /** + * 算法参数 + */ + private AlgorithmParameterSpec params; + /** + * 随机数生成器,可自定义随机数种子 + */ + private SecureRandom random; + + /** + * 构造 + * + * @param algorithm 算法名称 + */ + public CipherWrapper(String algorithm) { + this(SecureUtil.createCipher(algorithm)); + } + + /** + * 构造 + * + * @param cipher {@link Cipher} + */ + public CipherWrapper(Cipher cipher) { + this.cipher = cipher; + } + + /** + * 获取{@link AlgorithmParameterSpec}
+ * 在某些算法中,需要特别的参数,例如在ECIES中,此处为IESParameterSpec + * + * @return {@link AlgorithmParameterSpec} + */ + public AlgorithmParameterSpec getParams() { + return this.params; + } + + /** + * 设置 {@link AlgorithmParameterSpec},通常用于加盐或偏移向量 + * + * @param params {@link AlgorithmParameterSpec} + * @return this + */ + public CipherWrapper setParams(AlgorithmParameterSpec params) { + this.params = params; + return this; + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + */ + public CipherWrapper setRandom(SecureRandom random) { + this.random = random; + return this; + } + + /** + * 获取被包装的{@link Cipher} + * + * @return {@link Cipher} + */ + public Cipher getCipher() { + return this.cipher; + } + + /** + * 初始化{@link Cipher}为加密或者解密模式 + * + * @param mode 模式,见{@link Cipher#ENCRYPT_MODE} 或 {@link Cipher#DECRYPT_MODE} + * @param key 密钥 + * @return this + * @throws InvalidKeyException 无效key + * @throws InvalidAlgorithmParameterException 无效算法 + */ + public CipherWrapper initMode(int mode, Key key) + throws InvalidKeyException, InvalidAlgorithmParameterException { + final Cipher cipher = this.cipher; + final AlgorithmParameterSpec params = this.params; + final SecureRandom random = this.random; + if (null != params) { + if (null != random) { + cipher.init(mode, key, params, random); + } else { + cipher.init(mode, key, params); + } + } else { + if (null != random) { + cipher.init(mode, key, random); + } else { + cipher.init(mode, key); + } + } + return this; + } +} diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java index 22198e58a..25f5661c4 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java @@ -2,6 +2,7 @@ package cn.hutool.crypto.asymmetric; import cn.hutool.core.codec.Base64; import cn.hutool.core.io.FastByteArrayOutputStream; +import cn.hutool.crypto.CipherWrapper; import cn.hutool.crypto.CryptoException; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.SecureUtil; @@ -16,6 +17,7 @@ import java.security.InvalidKeyException; import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; /** @@ -37,7 +39,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto /** * Cipher负责完成加密或解密工作 */ - protected Cipher cipher; + protected CipherWrapper cipherWrapper; /** * 加密的块大小 @@ -47,12 +49,6 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * 解密的块大小 */ protected int decryptBlockSize = -1; - - /** - * 算法参数 - */ - private AlgorithmParameterSpec algorithmParameterSpec; - // ------------------------------------------------------------------ Constructor start /** @@ -201,7 +197,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public AlgorithmParameterSpec getAlgorithmParameterSpec() { - return algorithmParameterSpec; + return this.cipherWrapper.getParams(); } /** @@ -212,7 +208,19 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public void setAlgorithmParameterSpec(AlgorithmParameterSpec algorithmParameterSpec) { - this.algorithmParameterSpec = algorithmParameterSpec; + this.cipherWrapper.setParams(algorithmParameterSpec); + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + * @since 5.7.17 + */ + public AsymmetricCrypto setRandom(SecureRandom random) { + this.cipherWrapper.setRandom(random); + return this; } @Override @@ -229,11 +237,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto final Key key = getKeyByType(keyType); lock.lock(); try { - initMode(Cipher.ENCRYPT_MODE, key); + final Cipher cipher = initMode(Cipher.ENCRYPT_MODE, key); if (this.encryptBlockSize < 0) { // 在引入BC库情况下,自动获取块大小 - final int blockSize = this.cipher.getBlockSize(); + final int blockSize = cipher.getBlockSize(); if (blockSize > 0) { this.encryptBlockSize = blockSize; } @@ -254,11 +262,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto final Key key = getKeyByType(keyType); lock.lock(); try { - initMode(Cipher.DECRYPT_MODE, key); + final Cipher cipher = initMode(Cipher.DECRYPT_MODE, key); if (this.decryptBlockSize < 0) { // 在引入BC库情况下,自动获取块大小 - final int blockSize = this.cipher.getBlockSize(); + final int blockSize = cipher.getBlockSize(); if (blockSize > 0) { this.decryptBlockSize = blockSize; } @@ -281,7 +289,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public Cipher getCipher() { - return cipher; + return this.cipherWrapper.getCipher(); } /** @@ -290,7 +298,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 4.5.2 */ protected void initCipher() { - this.cipher = SecureUtil.createCipher(algorithm); + this.cipherWrapper = new CipherWrapper(this.algorithm); } /** @@ -309,7 +317,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto // 不足分段 if (dataLength <= maxBlockSize) { - return this.cipher.doFinal(data, 0, dataLength); + return getCipher().doFinal(data, 0, dataLength); } // 分段解密 @@ -337,7 +345,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto // 对数据分段处理 while (remainLength > 0) { blockSize = Math.min(remainLength, maxBlockSize); - out.write(cipher.doFinal(data, offSet, blockSize)); + out.write(getCipher().doFinal(data, offSet, blockSize)); offSet += blockSize; remainLength = dataLength - offSet; @@ -351,14 +359,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * * @param mode 模式,可选{@link Cipher#ENCRYPT_MODE}或者{@link Cipher#DECRYPT_MODE} * @param key 密钥 + * @return {@link Cipher} * @throws InvalidAlgorithmParameterException 异常算法错误 * @throws InvalidKeyException 异常KEY错误 */ - private void initMode(int mode, Key key) throws InvalidAlgorithmParameterException, InvalidKeyException { - if (null != this.algorithmParameterSpec) { - cipher.init(mode, key, this.algorithmParameterSpec); - } else { - cipher.init(mode, key); - } + private Cipher initMode(int mode, Key key) throws InvalidAlgorithmParameterException, InvalidKeyException { + return this.cipherWrapper.initMode(mode, key).getCipher(); } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java index ba92ab553..ea4a7756d 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java @@ -3,15 +3,16 @@ package cn.hutool.crypto.symmetric; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.CipherMode; +import cn.hutool.crypto.CipherWrapper; import cn.hutool.crypto.CryptoException; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.Padding; -import cn.hutool.crypto.SecureUtil; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -25,6 +26,7 @@ import java.io.OutputStream; import java.io.Serializable; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -40,18 +42,11 @@ import java.util.concurrent.locks.ReentrantLock; public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, Serializable { private static final long serialVersionUID = 1L; + private CipherWrapper cipherWrapper; /** * SecretKey 负责保存对称密钥 */ private SecretKey secretKey; - /** - * Cipher负责完成加密或解密工作 - */ - private Cipher cipher; - /** - * 加密解密参数 - */ - private AlgorithmParameterSpec params; /** * 是否0填充 */ @@ -152,7 +147,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, this.isZeroPadding = true; } - this.cipher = SecureUtil.createCipher(algorithm); + this.cipherWrapper = new CipherWrapper(algorithm); return this; } @@ -171,7 +166,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 加密或解密 */ public Cipher getCipher() { - return cipher; + return cipherWrapper.getCipher(); } /** @@ -181,7 +176,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setParams(AlgorithmParameterSpec params) { - this.params = params; + this.cipherWrapper.setParams(params); return this; } @@ -192,8 +187,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setIv(IvParameterSpec iv) { - setParams(iv); - return this; + return setParams(iv); } /** @@ -203,7 +197,18 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setIv(byte[] iv) { - setIv(new IvParameterSpec(iv)); + return setIv(new IvParameterSpec(iv)); + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + * @since 5.7.17 + */ + public SymmetricCrypto setRandom(SecureRandom random){ + this.cipherWrapper.setRandom(random); return this; } @@ -237,6 +242,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @since 5.6.8 */ public byte[] update(byte[] data) { + final Cipher cipher = cipherWrapper.getCipher(); lock.lock(); try { return cipher.update(paddingDataWithZero(data, cipher.getBlockSize())); @@ -376,11 +382,8 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, */ private SymmetricCrypto initParams(String algorithm, AlgorithmParameterSpec paramsSpec) { if (null == paramsSpec) { - byte[] iv = null; - final Cipher cipher = this.cipher; - if (null != cipher) { - iv = cipher.getIV(); - } + byte[] iv = Opt.ofNullable(cipherWrapper) + .map(CipherWrapper::getCipher).map(Cipher::getIV).get(); // 随机IV if (StrUtil.startWithIgnoreCase(algorithm, "PBE")) { @@ -409,13 +412,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @throws InvalidAlgorithmParameterException 无效算法 */ private Cipher initMode(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException { - final Cipher cipher = this.cipher; - if (null == this.params) { - cipher.init(mode, secretKey); - } else { - cipher.init(mode, secretKey, params); - } - return cipher; + return this.cipherWrapper.initMode(mode, this.secretKey).getCipher(); } /**