diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index bee351f57..25f233bd9 100755 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -102,6 +102,8 @@ * [数学相关-MathUtil](core/数学/数学相关-MathUtil.md) * 线程和并发 * [线程工具-ThreadUtil](core/线程和并发/线程工具-ThreadUtil.md) + * [自定义线程池-ExecutorBuilder](core/线程和并发/自定义线程池-ExecutorBuilder.md) + * [高并发测试-ConcurrencyTester](core/线程和并发/高并发测试-ConcurrencyTester.md) * 图片 * [图片工具-ImgUtil](core/图片/图片工具-ImgUtil.md) * [图片编辑器-Img](core/图片/图片编辑器-Img.md) @@ -136,7 +138,8 @@ * [加密解密工具-SecureUtil](crypto/加密解密工具-SecureUtil.md) * [对称加密-SymmetricCrypto](crypto/对称加密-SymmetricCrypto.md) * [非对称加密-AsymmetricCrypto](crypto/非对称加密-AsymmetricCrypto.md) - * [摘要加密-Digester和HMac](crypto/摘要加密-Digester和HMac.md) + * [摘要加密-Digester](crypto/摘要加密-Digester.md) + * [消息认证码算法-HMac.md](crypto/消息认证码算法-HMac.md) * [签名和验证-Sign](crypto/签名和验证-Sign.md) * [国密算法工具-SmUtil](crypto/国密算法工具-SmUtil.md) * DFA查找(Hutool-dfa) diff --git "a/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md" "b/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md" index b16ce6e34..1cc805666 100644 --- "a/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md" +++ "b/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md" @@ -4,7 +4,22 @@ ## 使用 +1. 构建`ChineseDate`对象 + +`ChineseDate`表示了农历的对象,构建此对象既可以使用公历的日期,也可以使用农历的日期。 + +```java +//通过农历构建 +ChineseDate chineseDate = new ChineseDate(1992,12,14); + +//通过公历构建 +ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate("1993-01-06")); +``` + +2. 基本使用 + ```java +//通过公历构建 ChineseDate date = new ChineseDate(DateUtil.parseDate("2020-01-25")); // 一月 date.getChineseMonth(); @@ -14,10 +29,22 @@ date.getChineseMonthName(); date.getChineseDay(); // 庚子 date.getCyclical(); -// 鼠 +// 生肖:鼠 date.getChineseZodiac(); -// 春节 +// 传统节日(部分支持,逗号分隔):春节 date.getFestivals(); // 庚子鼠年 正月初一 date.toString(); +``` + +3. 获取天干地支 + +从`5.4.1`开始,Hutool支持天干地支的获取: + +```java +//通过公历构建 +ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate("2020-08-28")); + +// 庚子年甲申月癸卯日 +String cyclicalYMD = chineseDate.getCyclicalYMD(); ``` \ No newline at end of file diff --git "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md" "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md" index 6f7d041ae..1f132770a 100755 --- "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md" +++ "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md" @@ -2,7 +2,7 @@ 并发在Java中算是一个比较难理解和容易出问题的部分,而并发的核心在线程。好在从JDK1.5开始Java提供了`concurrent`包可以很好的帮我们处理大部分并发、异步等问题。 -不过,ExecutorService和Executors等众多概念依旧让我们使用这个包变得比较麻烦,如何才能隐藏这些概念?又如何用一个方法解决问题?`ThreadUtil`便为此而生。 +不过,`ExecutorService`和`Executors`等众多概念依旧让我们使用这个包变得比较麻烦,如何才能隐藏这些概念?又如何用一个方法解决问题?`ThreadUtil`便为此而生。 ## 原理 Hutool使用`GlobalThreadPool`持有一个全局的线程池,默认所有异步方法在这个线程池中执行。 diff --git "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240-ExecutorBuilder.md" "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240-ExecutorBuilder.md" new file mode 100644 index 000000000..e5b03dbf4 --- /dev/null +++ "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240-ExecutorBuilder.md" @@ -0,0 +1,81 @@ +## 由来 + +在JDK中,提供了`Executors`用于创建自定义的线程池对象`ExecutorService`,但是考虑到线程池中存在众多概念,这些概念通过不同的搭配实现灵活的线程管理策略,单独使用`Executors`无法满足需求,构建了`ExecutorBuilder`。 + +### 概念 + +- `corePoolSize` 初始池大小 +- `maxPoolSize` 最大池大小(允许同时执行的最大线程数) +- `workQueue` 队列,用于存在未执行的线程 +- `handler` 当线程阻塞(block)时的异常处理器,所谓线程阻塞即线程池和等待队列已满,无法处理线程时采取的策略 + +### 线程池对待线程的策略 + +1. 如果池中任务数 < corePoolSize -> 放入立即执行 +2. 如果池中任务数 > corePoolSize -> 放入队列等待 +3. 队列满 -> 新建线程立即执行 +4. 执行中的线程 > maxPoolSize -> 触发handler(RejectedExecutionHandler)异常 + +### workQueue线程池策略 + +- `SynchronousQueue` 它将任务直接提交给线程而不保持它们。当运行线程小于`maxPoolSize`时会创建新线程,否则触发异常策略 +- `LinkedBlockingQueue` 默认无界队列,当运行线程大于`corePoolSize`时始终放入此队列,此时`maxPoolSize`无效。当构造LinkedBlockingQueue对象时传入参数,变为有界队列,队列满时,运行线程小于`maxPoolSize`时会创建新线程,否则触发异常策略 +- `ArrayBlockingQueue` 有界队列,相对无界队列有利于控制队列大小,队列满时,运行线程小于`maxPoolSize`时会创建新线程,否则触发异常策略 + +## 使用 + +1. 默认线程池 + +策略如下: + +- 初始线程数为corePoolSize指定的大小 +- 没有最大线程数限制 +- 默认使用LinkedBlockingQueue,默认队列大小为1024(最大等待数1024) +- 当运行线程大于corePoolSize放入队列,队列满后抛出异常 + +```java +ExecutorService executor = ExecutorBuilder builder = ExecutorBuilder.create()..build(); +``` + +2. 单线程线程池 + +- 初始线程数为 1 +- 最大线程数为 1 +- 默认使用LinkedBlockingQueue,默认队列大小为1024 +- 同时只允许一个线程工作,剩余放入队列等待,等待数超过1024报错 + +```java +ExecutorService executor = ExecutorBuilder.create()// + .setCorePoolSize(1)// + .setMaxPoolSize(1)// + .setKeepAliveTime(0)// + .build(); +``` + +3. 更多选项的线程池 + +- 初始5个线程 +- 最大10个线程 +- 有界等待队列,最大等待数是100 + +```java +ExecutorService executor = ExecutorBuilder.create() + .setCorePoolSize(5) + .setMaxPoolSize(10) + .setWorkQueue(new LinkedBlockingQueue<>(100)) + .build(); +``` + +3. 特殊策略的线程池 + +- 初始5个线程 +- 最大10个线程 +- 它将任务直接提交给线程而不保持它们。当运行线程小于maxPoolSize时会创建新线程,否则触发异常策略 + +```java +ExecutorService executor = ExecutorBuilder.create() + .setCorePoolSize(5) + .setMaxPoolSize(10) + .useSynchronousQueue() + .build(); +``` \ No newline at end of file diff --git "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\351\253\230\345\271\266\345\217\221\346\265\213\350\257\225-ConcurrencyTester.md" "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\351\253\230\345\271\266\345\217\221\346\265\213\350\257\225-ConcurrencyTester.md" new file mode 100644 index 000000000..b939d0007 --- /dev/null +++ "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\351\253\230\345\271\266\345\217\221\346\265\213\350\257\225-ConcurrencyTester.md" @@ -0,0 +1,17 @@ +## 由来 + +很多时候,我们需要简单模拟N个线程调用某个业务测试其并发状况,于是Hutool提供了一个简单的并发测试类——ConcurrencyTester。 + +## 使用 + +```java +ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> { + // 测试的逻辑内容 + long delay = RandomUtil.randomLong(100, 1000); + ThreadUtil.sleep(delay); + Console.log("{} test finished, delay: {}", Thread.currentThread().getName(), delay); +}); + +// 获取总的执行时间,单位毫秒 +Console.log(tester.getInterval()); +``` diff --git "a/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md" "b/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md" index 1babb65e8..5a9739bf6 100755 --- "a/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md" +++ "b/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md" @@ -15,14 +15,14 @@ Hutool针对`Bouncy Castle`做了简化包装,用于实现国密算法中的SM ```xml - org.bouncycastle - bcprov-jdk15on - ${bouncycastle.version} + org.bouncycastle + bcprov-jdk15to18 + 1.66 ``` > 说明 -> `bcprov-jdk15on`的版本请前往Maven中央库搜索,查找对应JDK的版本。 +> `bcprov-jdk15to18`的版本请前往Maven中央库搜索,查找对应JDK的最新版本。 ### 非对称加密SM2 @@ -52,6 +52,49 @@ String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey); String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey)); ``` +3. SM2签名和验签 + +```java +String content = "我是Hanley."; +final SM2 sm2 = SmUtil.sm2(); +String sign = sm2.signHex(HexUtil.encodeHexStr(content)); + +// true +boolean verify = sm2.verifyHex(HexUtil.encodeHexStr(content), sign); +``` + +当然,也可以自定义密钥对: + +```java +String content = "我是Hanley."; +KeyPair pair = SecureUtil.generateKeyPair("SM2"); +final SM2 sm2 = new SM2(pair.getPrivate(), pair.getPublic()); + +byte[] sign = sm2.sign(content.getBytes()); + +// true +boolean verify = sm2.verify(content.getBytes(), sign); +``` + +4. 使用SM2曲线点构建SM2 + +使用曲线点构建中的点生成和验证见:[https://i.goto327.top/CryptTools/SM2.aspx?tdsourcetag=s_pctim_aiomsg](https://i.goto327.top/CryptTools/SM2.aspx?tdsourcetag=s_pctim_aiomsg) + +```java +String privateKeyHex = "FAB8BBE670FAE338C9E9382B9FB6485225C11A3ECB84C938F10F20A93B6215F0"; +String x = "9EF573019D9A03B16B0BE44FC8A5B4E8E098F56034C97B312282DD0B4810AFC3"; +String y = "CC759673ED0FC9B9DC7E6FA38F0E2B121E02654BF37EA6B63FAF2A0D6013EADF"; + +// 数据和ID此处使用16进制表示 +String dataHex = "434477813974bf58f94bcf760833c2b40f77a5fc360485b0b9ed1bd9682edb45"; +String idHex = "31323334353637383132333435363738"; + +final SM2 sm2 = new SM2(privateKeyHex, x, y); +final String sign = sm2.signHex(data, id); +// true +boolean verify = sm2.verifyHex(data, sign) +``` + ### 摘要加密算法SM3 ```java diff --git "a/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester\345\222\214HMac.md" "b/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester.md" old mode 100755 new mode 100644 similarity index 60% rename from "docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester\345\222\214HMac.md" rename to "docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester.md" index 191201f41..d78d241e7 --- "a/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester\345\222\214HMac.md" +++ "b/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester.md" @@ -6,11 +6,10 @@ 但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证。 -### HMAC介绍 -HMAC,全称为“Hash Message Authentication Code”,中文名“散列消息鉴别码”,主要是利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。一般的,消息鉴别码用于验证传输于两个共 同享有一个密钥的单位之间的消息。HMAC 可以与任何迭代散列函数捆绑使用。MD5 和 SHA-1 就是这种散列函数。HMAC 还可以使用一个用于计算和确认消息鉴别值的密钥。 - ## Hutool支持的摘要算法类型 +在不引入第三方库的情况下,JDK支持有限的摘要算法: + 详细见:[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest) ### 摘要算法 @@ -21,59 +20,38 @@ HMAC,全称为“Hash Message Authentication Code”,中文名“散列消 - SHA-384 - SHA-512 -### Hmac算法 -- HmacMD5 -- HmacSHA1 -- HmacSHA256 -- HmacSHA384 -- HmacSHA512 - -## 摘要算法抽象 - -摘要对象被抽象为两个对象: -- Digester -- HMac - ## 使用 + ### Digester 以MD5为例: ```java Digester md5 = new Digester(DigestAlgorithm.MD5); -String digestHex = md5.digestHex(testStr);//5393554e94bf0eb6436f240a4fd71282 + +// 5393554e94bf0eb6436f240a4fd71282 +String digestHex = md5.digestHex(testStr); ``` 当然,做为最为常用的方法,MD5等方法被封装为工具方法在`DigestUtil`中,以上代码可以进一步简化为: ```java +// 5393554e94bf0eb6436f240a4fd71282 String md5Hex1 = DigestUtil.md5Hex(testStr); -//Junit单元测试 -//Assert.assertEquals("5393554e94bf0eb6436f240a4fd71282", md5Hex1); ``` -### HMac - -以HmacMD5为例: -```java -String testStr = "test中文"; - -byte[] key = "password".getBytes(); -HMac mac = new HMac(HmacAlgorithm.HmacMD5, key); - -String macHex1 = mac.digestHex(testStr);//b977f4b13f93f549e06140971bded384 -``` +## 更多摘要算法 ### SM3 -在4.2.1之后,Hutool借助Bouncy Castle库可以支持国密算法,以SM3为例: +在`4.2.1`之后,Hutool借助`Bouncy Castle`库可以支持国密算法,以SM3为例: 我们首先需要引入Bouncy Castle库: ```xml org.bouncycastle - bcpkix-jdk15on - 1.60 + bcprov-jdk15to18 + 1.66 ``` @@ -81,6 +59,9 @@ String macHex1 = mac.digestHex(testStr);//b977f4b13f93f549e06140971bded384 ```java Digester digester = DigestUtil.digester("sm3"); + //136ce3c86e4ed909b76082055a61586af20b4dab674732ebd4b599eef080c9be String digestHex = digester.digestHex("aaaaa"); -``` \ No newline at end of file +``` + +> Java标准库的`java.security`包提供了一种标准机制,允许第三方提供商无缝接入。当引入`Bouncy Castle`库的jar后,Hutool会自动检测并接入。具体方法可见`SecureUtil.createMessageDigest`。 \ No newline at end of file diff --git "a/docs/crypto/\346\266\210\346\201\257\350\256\244\350\257\201\347\240\201\347\256\227\346\263\225-HMac.md" "b/docs/crypto/\346\266\210\346\201\257\350\256\244\350\257\201\347\240\201\347\256\227\346\263\225-HMac.md" new file mode 100644 index 000000000..1aeac44a3 --- /dev/null +++ "b/docs/crypto/\346\266\210\346\201\257\350\256\244\350\257\201\347\240\201\347\256\227\346\263\225-HMac.md" @@ -0,0 +1,40 @@ +## 介绍 + +### HMAC介绍 +HMAC,全称为“Hash Message Authentication Code”,中文名“散列消息鉴别码”,主要是利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。一般的,消息鉴别码用于验证传输于两个共 同享有一个密钥的单位之间的消息。HMAC 可以与任何迭代散列函数捆绑使用。MD5 和 SHA-1 就是这种散列函数。HMAC 还可以使用一个用于计算和确认消息鉴别值的密钥。 + +## Hutool支持的算法类型 + +### Hmac算法 + +在不引入第三方库的情况下,JDK支持有限的摘要算法: + +- HmacMD5 +- HmacSHA1 +- HmacSHA256 +- HmacSHA384 +- HmacSHA512 + +## 使用 + +### HMac + +以HmacMD5为例: +```java +String testStr = "test中文"; + +// 此处密钥如果有非ASCII字符,考虑编码 +byte[] key = "password".getBytes(); +HMac mac = new HMac(HmacAlgorithm.HmacMD5, key); + +// b977f4b13f93f549e06140971bded384 +String macHex1 = mac.digestHex(testStr); +``` + +## 更多HMac算法 + +与摘要算法类似,通过加入`Bouncy Castle`库可以调用更多算法,使用也类似: + +```java +HMac mac = new HMac("XXXX", key); +``` \ No newline at end of file diff --git "a/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md" "b/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md" index deb3aeb16..6a7c28230 100755 --- "a/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md" +++ "b/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md" @@ -1,9 +1,9 @@ ## 介绍 Hutool针对`java.security.Signature`做了简化包装,包装类为:`Sign`,用于生成签名和签名验证。 -对于签名算法,Hutool封装了JDK的,具体介绍见:[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature): +对于签名算法,Hutool封装了JDK的Signature,具体介绍见:[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature): -``` +```java // The RSA signature algorithm NONEwithRSA diff --git "a/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md" "b/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md" index 674b31f5f..702ac436c 100755 --- "a/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md" +++ "b/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md" @@ -10,14 +10,15 @@ Hutool封装了JDK的,详细见[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator): - RSA -- DSA -- EC +- RSA_ECB_PKCS1(RSA/ECB/PKCS1Padding) +- RSA_None(RSA/None/NoPadding) +- ECIES(需要Bouncy Castle库) ## 使用 在非对称加密中,我们可以通过`AsymmetricCrypto(AsymmetricAlgorithm algorithm)`构造方法,通过传入不同的算法枚举,获得其加密解密器。 -当然,为了方便,我们针对最常用的RSA和DSA算法构建了单独的对象:`RSA`和`DSA`。 +当然,为了方便,我们针对最常用的RSA算法构建了单独的对象:`RSA`。 ### 基本使用 @@ -93,3 +94,33 @@ byte[] decrypt = rsa.decrypt(aByte, KeyType.PrivateKey); //Assert.assertEquals("虎头闯杭州,多抬头看天,切勿只管种地", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8)); ``` +## 其它算法 + +### ECIES + +ECIES全称集成加密方案(elliptic curve integrate encrypt scheme) + +Hutool借助`Bouncy Castle`库可以支持`ECIES`算法: + +我们首先需要引入Bouncy Castle库: + +```xml + + org.bouncycastle + bcprov-jdk15to18 + 1.66 + +``` + +```java +final ECIES ecies = new ECIES(); +String textBase = "我是一段特别长的测试"; +StringBuilder text = new StringBuilder(); +for (int i = 0; i < 10; i++) { + text.append(textBase); +} + +// 公钥加密,私钥解密 +String encryptStr = ecies.encryptBase64(text.toString(), KeyType.PublicKey); +String decryptStr = StrUtil.utf8Str(ecies.decrypt(encryptStr, KeyType.PrivateKey)); +``` \ No newline at end of file