最近在使用javax.crypto.Mac做加解密时,发现用压测工具做高并发的压测时会出问题。查了一下原因:为了优化性能,全局只提前为每一秘钥生成了一个Mac类,而这个类在进行计算时会改变内部数据结构,所以在高并发的时候会报错。查到和想到的类似问题解决办法有两个:
1、每次使用时,clone一个Mac类
2、使用threadlocal为每个线程新建一个Mac类
下面是我用threadlocal的例子:
import com.google.common.cache.Cache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
/**
* 签名工具
*/
@Slf4j
public class SignUtil {
private static ThreadLocal<Cache<String, Optional<Mac>>> macThreadLocal = ThreadLocal.withInitial(() -> CacheBuilder.newBuilder().maximumSize(1000).build());
/**
* 计算签名
*
* @param secret APP密钥
* @param method HttpMethod
* @param path
* @param headers
* @param querys
* @param bodys
* @param signHeaderPrefixList 自定义参与签名Header前缀
* @return 签名后的字符串
*/
public static String sign(String secret, String method, String path,
Map<String, String> headers,
Map<String, String> querys,
Map<String, String> bodys,
List<String> signHeaderPrefixList) {
try {
Mac hmacSha256 = macThreadLocal.get().get(secret, () -> {
Mac hmacSha2561 = null;
try {
hmacSha2561 = Mac.getInstance(Constants.HMAC_SHA256);
byte[] keyBytes = secret.getBytes(Constants.ENCODING);
hmacSha2561.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, Constants.HMAC_SHA256));
} catch (Exception e) {
log.error("get hmacSha256 error", e);
}
return Optional.ofNullable(hmacSha2561);
}).get();
return new String(Base64.encodeBase64(
hmacSha256.doFinal(buildStringToSign(method, path, headers, querys, bodys, signHeaderPrefixList)
.getBytes(Constants.ENCODING))),
Constants.ENCODING);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}