diff --git a/CHANGELOG.md b/CHANGELOG.md index f0911dd7c..7775b6d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * 【core】 XmlUtil中mapToStr支持namespace(pr#599@Github) * 【core】 ZipUtil修改策略:默认关闭输入流(issue#604@Github) * 【core】 改进CsvReader,支持RowHandler按行处理(issue#608@Github) +* 【core】 增加MapUtil.sortJoin,改进SecureUtil.signParams支持补充字符串(issue#606@Github) ### Bug修复 * 【core】 解决ConcurrentHashSet不能序列化的问题(issue#600@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java index c94f49a4a..fd9fb86db 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java @@ -492,11 +492,28 @@ public class MapUtil { * @param map Map * @param separator entry之间的连接符 * @param keyValueSeparator kv之间的连接符 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 连接字符串 * @since 3.1.1 */ - public static String join(Map map, String separator, String keyValueSeparator) { - return join(map, separator, keyValueSeparator, false); + public static String join(Map map, String separator, String keyValueSeparator, String... otherParams) { + return join(map, separator, keyValueSeparator, false, otherParams); + } + + /** + * 根据参数排序后拼接为字符串,常用于签名 + * + * @param params 参数 + * @param separator entry之间的连接符 + * @param keyValueSeparator kv之间的连接符 + * @param isIgnoreNull 是否忽略null的键和值 + * @param otherParams 其它附加参数字符串(例如密钥) + * @return 签名字符串 + * @since 5.0.4 + */ + public static String sortJoin(Map params, String separator, String keyValueSeparator, boolean isIgnoreNull, + String... otherParams) { + return join(sort(params), separator, keyValueSeparator, isIgnoreNull, otherParams); } /** @@ -507,11 +524,12 @@ public class MapUtil { * @param map Map * @param separator entry之间的连接符 * @param keyValueSeparator kv之间的连接符 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 连接后的字符串 * @since 3.1.1 */ - public static String joinIgnoreNull(Map map, String separator, String keyValueSeparator) { - return join(map, separator, keyValueSeparator, true); + public static String joinIgnoreNull(Map map, String separator, String keyValueSeparator, String... otherParams) { + return join(map, separator, keyValueSeparator, true, otherParams); } /** @@ -519,24 +537,33 @@ public class MapUtil { * * @param 键类型 * @param 值类型 - * @param map Map + * @param map Map,为空返回otherParams拼接 * @param separator entry之间的连接符 * @param keyValueSeparator kv之间的连接符 * @param isIgnoreNull 是否忽略null的键和值 - * @return 连接后的字符串 + * @param otherParams 其它附加参数字符串(例如密钥) + * @return 连接后的字符串,map和otherParams为空返回"" * @since 3.1.1 */ - public static String join(Map map, String separator, String keyValueSeparator, boolean isIgnoreNull) { + public static String join(Map map, String separator, String keyValueSeparator, boolean isIgnoreNull, String... otherParams) { final StringBuilder strBuilder = StrUtil.builder(); boolean isFirst = true; - for (Entry entry : map.entrySet()) { - if (false == isIgnoreNull || entry.getKey() != null && entry.getValue() != null) { - if (isFirst) { - isFirst = false; - } else { - strBuilder.append(separator); + if(isNotEmpty(map)){ + for (Entry entry : map.entrySet()) { + if (false == isIgnoreNull || entry.getKey() != null && entry.getValue() != null) { + if (isFirst) { + isFirst = false; + } else { + strBuilder.append(separator); + } + strBuilder.append(Convert.toStr(entry.getKey())).append(keyValueSeparator).append(Convert.toStr(entry.getValue())); } - strBuilder.append(Convert.toStr(entry.getKey())).append(keyValueSeparator).append(Convert.toStr(entry.getValue())); + } + } + // 补充其它字符串到末尾,默认无分隔符 + if (ArrayUtil.isNotEmpty(otherParams)) { + for (String otherParam : otherParams) { + strBuilder.append(otherParam); } } return strBuilder.toString(); @@ -687,13 +714,17 @@ public class MapUtil { * * @param key的类型 * @param value的类型 - * @param map Map + * @param map Map,为null返回null * @param comparator Key比较器 - * @return TreeMap + * @return TreeMap,map为null返回null * @see #newTreeMap(Map, Comparator) * @since 4.0.1 */ public static TreeMap sort(Map map, Comparator comparator) { + if(null == map){ + return null; + } + TreeMap result; if (map instanceof TreeMap) { // 已经是可排序Map,此时只有比较器一致才返回原map diff --git a/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java index 1356c1ae9..1cde459ac 100644 --- a/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java @@ -1,14 +1,15 @@ package cn.hutool.core.map; -import java.util.Map; -import java.util.Map.Entry; - -import org.junit.Assert; -import org.junit.Test; - import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Editor; import cn.hutool.core.lang.Filter; +import cn.hutool.core.util.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; public class MapUtilTest { @@ -20,16 +21,7 @@ public class MapUtilTest { map.put("c", "3"); map.put("d", "4"); - Map map2 = MapUtil.filter(map, new Filter>() { - - @Override - public boolean accept(Entry t) { - if (Convert.toInt(t.getValue()) % 2 == 0) { - return true; - } - return false; - } - }); + Map map2 = MapUtil.filter(map, (Filter>) t -> Convert.toInt(t.getValue()) % 2 == 0); Assert.assertEquals(2, map2.size()); @@ -45,14 +37,10 @@ public class MapUtilTest { map.put("c", "3"); map.put("d", "4"); - Map map2 = MapUtil.filter(map, new Editor>() { - - @Override - public Entry edit(Entry t) { - // 修改每个值使之*10 - t.setValue(t.getValue() + "0"); - return t; - } + Map map2 = MapUtil.filter(map, (Editor>) t -> { + // 修改每个值使之*10 + t.setValue(t.getValue() + "0"); + return t; }); Assert.assertEquals(4, map2.size()); @@ -97,4 +85,21 @@ public class MapUtilTest { Assert.assertEquals("d", objectArray[3][0]); Assert.assertEquals("4", objectArray[3][1]); } + + @Test + public void sortJoinTest(){ + Map build = MapUtil.builder(new HashMap()) + .put("key1", "value1") + .put("key3", "value3") + .put("key2", "value2").build(); + + String join1 = MapUtil.sortJoin(build, StrUtil.EMPTY, StrUtil.EMPTY, false); + Assert.assertEquals("key1value1key2value2key3value3", join1); + + String join2 = MapUtil.sortJoin(build, StrUtil.EMPTY, StrUtil.EMPTY, false, "123"); + Assert.assertEquals("key1value1key2value2key3value3123", join2); + + String join3 = MapUtil.sortJoin(build, StrUtil.EMPTY, StrUtil.EMPTY, false, "123", "abc"); + Assert.assertEquals("key1value1key2value2key3value3123abc", join3); + } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java index 8ff5ba0b1..3359dd72e 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java @@ -24,6 +24,7 @@ import cn.hutool.core.codec.Base64; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Validator; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; @@ -825,13 +826,14 @@ public final class SecureUtil { * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值 * - * @param crypto 对称加密算法 - * @param params 参数 + * @param crypto 对称加密算法 + * @param params 参数 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 签名 * @since 4.0.1 */ - public static String signParams(SymmetricCrypto crypto, Map params) { - return signParams(crypto, params, StrUtil.EMPTY, StrUtil.EMPTY, true); + public static String signParams(SymmetricCrypto crypto, Map params, String... otherParams) { + return signParams(crypto, params, StrUtil.EMPTY, StrUtil.EMPTY, true, otherParams); } /** @@ -843,15 +845,13 @@ public final class SecureUtil { * @param separator entry之间的连接符 * @param keyValueSeparator kv之间的连接符 * @param isIgnoreNull 是否忽略null的键和值 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 签名 * @since 4.0.1 */ - public static String signParams(SymmetricCrypto crypto, Map params, String separator, String keyValueSeparator, boolean isIgnoreNull) { - if (MapUtil.isEmpty(params)) { - return null; - } - String paramsStr = MapUtil.join(MapUtil.sort(params), separator, keyValueSeparator, isIgnoreNull); - return crypto.encryptHex(paramsStr); + public static String signParams(SymmetricCrypto crypto, Map params, String separator, + String keyValueSeparator, boolean isIgnoreNull, String... otherParams) { + return crypto.encryptHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams)); } /** @@ -859,12 +859,13 @@ public final class SecureUtil { * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值 * - * @param params 参数 + * @param params 参数 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 签名 * @since 4.0.1 */ - public static String signParamsMd5(Map params) { - return signParams(DigestAlgorithm.MD5, params); + public static String signParamsMd5(Map params, String... otherParams) { + return signParams(DigestAlgorithm.MD5, params, otherParams); } /** @@ -872,12 +873,13 @@ public final class SecureUtil { * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值 * - * @param params 参数 + * @param params 参数 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 签名 * @since 4.0.8 */ - public static String signParamsSha1(Map params) { - return signParams(DigestAlgorithm.SHA1, params); + public static String signParamsSha1(Map params, String... otherParams) { + return signParams(DigestAlgorithm.SHA1, params, otherParams); } /** @@ -885,12 +887,13 @@ public final class SecureUtil { * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串
* 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值 * - * @param params 参数 + * @param params 参数 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 签名 * @since 4.0.1 */ - public static String signParamsSha256(Map params) { - return signParams(DigestAlgorithm.SHA256, params); + public static String signParamsSha256(Map params, String... otherParams) { + return signParams(DigestAlgorithm.SHA256, params, otherParams); } /** @@ -900,11 +903,12 @@ public final class SecureUtil { * * @param digestAlgorithm 摘要算法 * @param params 参数 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 签名 * @since 4.0.1 */ - public static String signParams(DigestAlgorithm digestAlgorithm, Map params) { - return signParams(digestAlgorithm, params, StrUtil.EMPTY, StrUtil.EMPTY, true); + public static String signParams(DigestAlgorithm digestAlgorithm, Map params, String... otherParams) { + return signParams(digestAlgorithm, params, StrUtil.EMPTY, StrUtil.EMPTY, true, otherParams); } /** @@ -916,15 +920,13 @@ public final class SecureUtil { * @param separator entry之间的连接符 * @param keyValueSeparator kv之间的连接符 * @param isIgnoreNull 是否忽略null的键和值 + * @param otherParams 其它附加参数字符串(例如密钥) * @return 签名 * @since 4.0.1 */ - public static String signParams(DigestAlgorithm digestAlgorithm, Map params, String separator, String keyValueSeparator, boolean isIgnoreNull) { - if (MapUtil.isEmpty(params)) { - return null; - } - final String paramsStr = MapUtil.join(MapUtil.sort(params), separator, keyValueSeparator, isIgnoreNull); - return new Digester(digestAlgorithm).digestHex(paramsStr); + public static String signParams(DigestAlgorithm digestAlgorithm, Map params, String separator, + String keyValueSeparator, boolean isIgnoreNull, String... otherParams) { + return new Digester(digestAlgorithm).digestHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams)); } // ------------------------------------------------------------------- UUID diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/SignTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/SignTest.java index d4074554d..33d88203e 100644 --- a/hutool-crypto/src/test/java/cn/hutool/crypto/test/SignTest.java +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/SignTest.java @@ -1,5 +1,6 @@ package cn.hutool.crypto.test; +import cn.hutool.core.map.MapUtil; import org.junit.Assert; import org.junit.Test; @@ -8,6 +9,9 @@ import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.Sign; import cn.hutool.crypto.asymmetric.SignAlgorithm; +import java.util.HashMap; +import java.util.Map; + /** * 签名单元测试 * @@ -88,4 +92,18 @@ public class SignTest { boolean verify = sign.verify(data, signed); Assert.assertTrue(verify); } + + @Test + public void signParamsTest(){ + Map build = MapUtil.builder(new HashMap()) + .put("key1", "value1") + .put("key2", "value2").build(); + + String sign1 = SecureUtil.signParamsSha1(build); + Assert.assertEquals("9ed30bfe2efbc7038a824b6c55c24a11bfc0dce5", sign1); + String sign2 = SecureUtil.signParamsSha1(build, "12345678"); + Assert.assertEquals("944b68d94c952ec178c4caf16b9416b6661f7720", sign2); + String sign3 = SecureUtil.signParamsSha1(build, "12345678", "abc"); + Assert.assertEquals("edee1b477af1b96ebd20fdf08d818f352928d25d", sign3); + } }