Looly 6 年 前
コミット
3511952a35

+ 1 - 0
CHANGELOG.md

@@ -10,6 +10,7 @@
 * 【core】       XmlUtil中mapToStr支持namespace(pr#599@Github)
 * 【core】       XmlUtil中mapToStr支持namespace(pr#599@Github)
 * 【core】       ZipUtil修改策略:默认关闭输入流(issue#604@Github)
 * 【core】       ZipUtil修改策略:默认关闭输入流(issue#604@Github)
 * 【core】       改进CsvReader,支持RowHandler按行处理(issue#608@Github)
 * 【core】       改进CsvReader,支持RowHandler按行处理(issue#608@Github)
+* 【core】       增加MapUtil.sortJoin,改进SecureUtil.signParams支持补充字符串(issue#606@Github)
 
 
 ### Bug修复
 ### Bug修复
 * 【core】       解决ConcurrentHashSet不能序列化的问题(issue#600@Github)
 * 【core】       解决ConcurrentHashSet不能序列化的问题(issue#600@Github)

+ 47 - 16
hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java

@@ -492,11 +492,28 @@ public class MapUtil {
 	 * @param map               Map
 	 * @param map               Map
 	 * @param separator         entry之间的连接符
 	 * @param separator         entry之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
+	 * @param otherParams       其它附加参数字符串(例如密钥)
 	 * @return 连接字符串
 	 * @return 连接字符串
 	 * @since 3.1.1
 	 * @since 3.1.1
 	 */
 	 */
-	public static <K, V> String join(Map<K, V> map, String separator, String keyValueSeparator) {
-		return join(map, separator, keyValueSeparator, false);
+	public static <K, V> String join(Map<K, V> 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 map               Map
 	 * @param separator         entry之间的连接符
 	 * @param separator         entry之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
+	 * @param otherParams       其它附加参数字符串(例如密钥)
 	 * @return 连接后的字符串
 	 * @return 连接后的字符串
 	 * @since 3.1.1
 	 * @since 3.1.1
 	 */
 	 */
-	public static <K, V> String joinIgnoreNull(Map<K, V> map, String separator, String keyValueSeparator) {
-		return join(map, separator, keyValueSeparator, true);
+	public static <K, V> String joinIgnoreNull(Map<K, V> map, String separator, String keyValueSeparator, String... otherParams) {
+		return join(map, separator, keyValueSeparator, true, otherParams);
 	}
 	}
 
 
 	/**
 	/**
@@ -519,24 +537,33 @@ public class MapUtil {
 	 *
 	 *
 	 * @param <K>               键类型
 	 * @param <K>               键类型
 	 * @param <V>               值类型
 	 * @param <V>               值类型
-	 * @param map               Map
+	 * @param map               Map,为空返回otherParams拼接
 	 * @param separator         entry之间的连接符
 	 * @param separator         entry之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param isIgnoreNull      是否忽略null的键和值
 	 * @param isIgnoreNull      是否忽略null的键和值
-	 * @return 连接后的字符串
+	 * @param otherParams       其它附加参数字符串(例如密钥)
+	 * @return 连接后的字符串,map和otherParams为空返回""
 	 * @since 3.1.1
 	 * @since 3.1.1
 	 */
 	 */
-	public static <K, V> String join(Map<K, V> map, String separator, String keyValueSeparator, boolean isIgnoreNull) {
+	public static <K, V> String join(Map<K, V> map, String separator, String keyValueSeparator, boolean isIgnoreNull, String... otherParams) {
 		final StringBuilder strBuilder = StrUtil.builder();
 		final StringBuilder strBuilder = StrUtil.builder();
 		boolean isFirst = true;
 		boolean isFirst = true;
-		for (Entry<K, V> 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<K, V> 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();
 		return strBuilder.toString();
@@ -687,13 +714,17 @@ public class MapUtil {
 	 *
 	 *
 	 * @param <K>        key的类型
 	 * @param <K>        key的类型
 	 * @param <V>        value的类型
 	 * @param <V>        value的类型
-	 * @param map        Map
+	 * @param map        Map,为null返回null
 	 * @param comparator Key比较器
 	 * @param comparator Key比较器
-	 * @return TreeMap
+	 * @return TreeMap,map为null返回null
 	 * @see #newTreeMap(Map, Comparator)
 	 * @see #newTreeMap(Map, Comparator)
 	 * @since 4.0.1
 	 * @since 4.0.1
 	 */
 	 */
 	public static <K, V> TreeMap<K, V> sort(Map<K, V> map, Comparator<? super K> comparator) {
 	public static <K, V> TreeMap<K, V> sort(Map<K, V> map, Comparator<? super K> comparator) {
+		if(null == map){
+			return null;
+		}
+
 		TreeMap<K, V> result;
 		TreeMap<K, V> result;
 		if (map instanceof TreeMap) {
 		if (map instanceof TreeMap) {
 			// 已经是可排序Map,此时只有比较器一致才返回原map
 			// 已经是可排序Map,此时只有比较器一致才返回原map

+ 29 - 24
hutool-core/src/test/java/cn/hutool/core/map/MapUtilTest.java

@@ -1,14 +1,15 @@
 package cn.hutool.core.map;
 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.convert.Convert;
 import cn.hutool.core.lang.Editor;
 import cn.hutool.core.lang.Editor;
 import cn.hutool.core.lang.Filter;
 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 {
 public class MapUtilTest {
 	
 	
@@ -20,16 +21,7 @@ public class MapUtilTest {
 		map.put("c", "3");
 		map.put("c", "3");
 		map.put("d", "4");
 		map.put("d", "4");
 
 
-		Map<String, String> map2 = MapUtil.filter(map, new Filter<Entry<String, String>>() {
-
-			@Override
-			public boolean accept(Entry<String, String> t) {
-				if (Convert.toInt(t.getValue()) % 2 == 0) {
-					return true;
-				}
-				return false;
-			}
-		});
+		Map<String, String> map2 = MapUtil.filter(map, (Filter<Entry<String, String>>) t -> Convert.toInt(t.getValue()) % 2 == 0);
 
 
 		Assert.assertEquals(2, map2.size());
 		Assert.assertEquals(2, map2.size());
 
 
@@ -45,14 +37,10 @@ public class MapUtilTest {
 		map.put("c", "3");
 		map.put("c", "3");
 		map.put("d", "4");
 		map.put("d", "4");
 
 
-		Map<String, String> map2 = MapUtil.filter(map, new Editor<Entry<String, String>>() {
-
-			@Override
-			public Entry<String, String> edit(Entry<String, String> t) {
-				// 修改每个值使之*10
-				t.setValue(t.getValue() + "0");
-				return t;
-			}
+		Map<String, String> map2 = MapUtil.filter(map, (Editor<Entry<String, String>>) t -> {
+			// 修改每个值使之*10
+			t.setValue(t.getValue() + "0");
+			return t;
 		});
 		});
 
 
 		Assert.assertEquals(4, map2.size());
 		Assert.assertEquals(4, map2.size());
@@ -97,4 +85,21 @@ public class MapUtilTest {
 		Assert.assertEquals("d", objectArray[3][0]);
 		Assert.assertEquals("d", objectArray[3][0]);
 		Assert.assertEquals("4", objectArray[3][1]);
 		Assert.assertEquals("4", objectArray[3][1]);
 	}
 	}
+
+	@Test
+	public void sortJoinTest(){
+		Map<String, String> build = MapUtil.builder(new HashMap<String, String>())
+				.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);
+	}
 }
 }

+ 29 - 27
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.io.FileUtil;
 import cn.hutool.core.lang.Validator;
 import cn.hutool.core.lang.Validator;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.HexUtil;
 import cn.hutool.core.util.HexUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
@@ -825,13 +826,14 @@ public final class SecureUtil {
 	 * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 *
 	 *
-	 * @param crypto 对称加密算法
-	 * @param params 参数
+	 * @param crypto      对称加密算法
+	 * @param params      参数
+	 * @param otherParams 其它附加参数字符串(例如密钥)
 	 * @return 签名
 	 * @return 签名
 	 * @since 4.0.1
 	 * @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 separator         entry之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param isIgnoreNull      是否忽略null的键和值
 	 * @param isIgnoreNull      是否忽略null的键和值
+	 * @param otherParams       其它附加参数字符串(例如密钥)
 	 * @return 签名
 	 * @return 签名
 	 * @since 4.0.1
 	 * @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的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 *
 	 *
-	 * @param params 参数
+	 * @param params      参数
+	 * @param otherParams 其它附加参数字符串(例如密钥)
 	 * @return 签名
 	 * @return 签名
 	 * @since 4.0.1
 	 * @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的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 *
 	 *
-	 * @param params 参数
+	 * @param params      参数
+	 * @param otherParams 其它附加参数字符串(例如密钥)
 	 * @return 签名
 	 * @return 签名
 	 * @since 4.0.8
 	 * @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的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 参数签名为对Map参数按照key的顺序排序后拼接为字符串,然后根据提供的签名算法生成签名字符串<br>
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 * 拼接后的字符串键值对之间无符号,键值对之间无符号,忽略null值
 	 *
 	 *
-	 * @param params 参数
+	 * @param params      参数
+	 * @param otherParams 其它附加参数字符串(例如密钥)
 	 * @return 签名
 	 * @return 签名
 	 * @since 4.0.1
 	 * @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 digestAlgorithm 摘要算法
 	 * @param params          参数
 	 * @param params          参数
+	 * @param otherParams     其它附加参数字符串(例如密钥)
 	 * @return 签名
 	 * @return 签名
 	 * @since 4.0.1
 	 * @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 separator         entry之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param keyValueSeparator kv之间的连接符
 	 * @param isIgnoreNull      是否忽略null的键和值
 	 * @param isIgnoreNull      是否忽略null的键和值
+	 * @param otherParams       其它附加参数字符串(例如密钥)
 	 * @return 签名
 	 * @return 签名
 	 * @since 4.0.1
 	 * @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
 	// ------------------------------------------------------------------- UUID

+ 18 - 0
hutool-crypto/src/test/java/cn/hutool/crypto/test/SignTest.java

@@ -1,5 +1,6 @@
 package cn.hutool.crypto.test;
 package cn.hutool.crypto.test;
 
 
+import cn.hutool.core.map.MapUtil;
 import org.junit.Assert;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.Test;
 
 
@@ -8,6 +9,9 @@ import cn.hutool.crypto.SecureUtil;
 import cn.hutool.crypto.asymmetric.Sign;
 import cn.hutool.crypto.asymmetric.Sign;
 import cn.hutool.crypto.asymmetric.SignAlgorithm;
 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);
 		boolean verify = sign.verify(data, signed);
 		Assert.assertTrue(verify);
 		Assert.assertTrue(verify);
 	}
 	}
+
+	@Test
+	public void signParamsTest(){
+		Map<String, String> build = MapUtil.builder(new HashMap<String, String>())
+				.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);
+	}
 }
 }