package com.zkml.common.util;
import com.google.common.hash.*;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;
/**
* 系统工具类
* 1获取UUID
* 2获取不撒盐的MD5及验证相等
* 3获取撒盐的MD5及验证相等
* <p>
* 加Salt可以一定程度上解决这一问题。所谓加Salt,就是加点“佐料”。其基本想法是这样的——当用户首次提供密码时(通常是注册时),由系统自动往这个密码里撒一些“佐料”,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的“佐料”,然后散列,再比较散列值,已确定密码是否正确。
* 这里的“佐料”被称作“Salt值”,这个值是由系统随机生成的,并且只有系统知道。这样,即便两个用户使用了同一个密码,由于系统为它们生成的salt值不同,他们的散列值也是不同的。即便黑客可以通过自己的密码和自己生成的散列值来找具有特定密码的用户,但这个几率太小了(密码和salt值都得和黑客使用的一样才行)。
* <p>
* 下面详细介绍一下加Salt散列的过程。介绍之前先强调一点,前面说过,验证密码时要使用和最初散列密码时使用“相同的”佐料。所以Salt值是要存放在数据库里的。
* <p>
* 用户注册时:
* 1)用户提供密码(以及其他用户信息);
* 2)系统为用户生成Salt值;
* 3)系统将Salt值和用户密码连接到一起;
* 4)对连接后的值进行散列,得到Hash值;
* 5)将Hash值和Salt值分别放到数据库中。
* <p>
* 登录时:
* 1)用户提供用户名和密码;
* 2)系统通过用户名找到与之对应的Hash值和Salt值;
* 3)系统将Salt值和用户提供的密码连接到一起;
* 4)对连接后的值进行散列,得到Hash'(注意有个“撇”);
* 5)比较Hash和Hash'是否相等,相等则表示密码正确,否则表示密码错误。
*
* @author : Unknow on 2019-04-20 18:01
*/
public final class SystemUtil {
/* a-z、A-Z、0-9 */
private final static String azAZ09 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* 获取uuid,格式不带杠
*
* @return uuid
*/
public static String getId() {
String uuid = UUID.randomUUID().toString().replaceAll("\-", "");
return uuid;
}
/**
* 使用Guava工具类生成MD5密码
* https://www.jianshu.com/p/2cf845e5121d
*
* @param paramStr 待加密的字符串的字节组,不可为空不可为null
* @return MD5加密后的值
*/
private static String getGuavaMD5(String paramStr) {
if (MyUtil.isBlank(paramStr)) {
throw new RuntimeException("paramStr不可为空");
}
HashFunction hf = Hashing.md5();
HashCode hc = hf.newHasher().putString(paramStr, Charset.defaultCharset()).hash();
return hc.toString().toUpperCase();
}
/**
* md5加密
*
* @param paramByte 待加密的字符串的字节组,不可为空不可为null
* @return MD5加密后的值
*/
public static String getMD5(byte[] paramByte) {
return getMD5(new String(paramByte));
}
/**
* md5加密
*
* @param paramStr 待加密的字符串,不可为空不可为null
* @return MD5加密后的值
*/
public static String getMD5(String paramStr) {
return getGuavaMD5(paramStr);
}
/**
* 校验字符串、需要对比的加盐MD5结果字符串
*
* @param paramStr 待加密的字符串,不可为空不可为null
* @param md5WithoutSalt 待用于比较的加密的字符串,不可为空不可为null
* @return paramStr加密后是否相等md5WithoutSalt
*/
public static boolean verifyMD5(String paramStr, String md5WithoutSalt) {
return getMD5(paramStr).equals(md5WithoutSalt);
}
/**
* 生成盐(salt)
*
* @return 用于MD5加密使用的盐(salt)(32位)
*/
public static String getSalt() {
char[] chars = azAZ09.toCharArray();
char[] saltChars = new char[32];
Random random = new SecureRandom();
for (int i = 0; i < 32; i++) {
int n = random.nextInt(62);
saltChars[i] = chars[n];
}
return new String(saltChars);
}
/**
* 带盐的md5加密
*
* @param paramStr 待加密的字符串,不可为空不可为null
* @param salt 盐,不可为空不可为null
* @return 带盐的MD5加密后的值
*/
public static String getMD5(String paramStr, String salt) {
if (MyUtil.isBlank(salt)) {
throw new RuntimeException("salt不可为空");
}
return getMD5(getMD5(paramStr) + salt);
}
/**
* 校验字符串、盐、需要对比的加盐MD5结果字符串
*
* @param paramStr 待加密的字符串,不可为空不可为null
* @param salt 盐,不可为空不可为null
* @param md5WithSalt 待用于比较的加密的字符串,不可为空不可为null
* @return (paramStr + salt)加密后是否相等(md5WithSalt)
*/
public static boolean verifyMD5(String paramStr, String salt, String md5WithSalt) {
return getMD5(paramStr, salt).equals(md5WithSalt.toUpperCase());
}
}