天天看點

SystemUtil

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());
    }
}