
commons.box.app.misc.Hash Maven / Gradle / Ivy
Show all versions of commons-box-app Show documentation
package commons.box.app.misc;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import commons.box.app.AppError;
import commons.box.app.AppLog;
import commons.box.util.Langs;
import commons.box.util.Logs;
import commons.box.util.Strs;
import jodd.crypt.BCrypt;
import java.io.Closeable;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.function.Supplier;
/**
* Hash机制 支持md5/sha/crc等
*/
@SuppressWarnings("UnstableApiUsage")
public final class Hash {
private static final AppLog LOG = Logs.get(Hash.class);
private static final int BUF_SIZE = 2048; // 2K缓冲区
public static final Hash CRC32 = new Hash(Hashing.crc32());
public static final Hash CRC32C = new Hash(Hashing.crc32c());
@SuppressWarnings("deprecation")
public static final Hash MD5 = new Hash(Hashing.md5());
@SuppressWarnings("deprecation")
public static final Hash SHA1 = new Hash(Hashing.sha1());
public static final Hash SHA256 = new Hash(Hashing.sha256());
public static final Hash SHA384 = new Hash(Hashing.sha384());
public static final Hash SHA512 = new Hash(Hashing.sha512());
public static final Hash MURMUR3_32 = new Hash(Hashing.murmur3_32());
public static final Hash MURMUR3_128 = new Hash(Hashing.murmur3_128());
public static final Hash SIP_HASH24 = new Hash(Hashing.sipHash24());
public static final Hash ADLER32 = new Hash(Hashing.adler32());
public static final BCryptCoder BCRYPT = new BCryptCoder();
private final HashFunction func;
private Hash(HashFunction func) {
this.func = func;
}
/**
* 获取 guava hasher对象 提供更多额外操作
*
* @return
* @throws AppError
*/
public final Coder coder() {
return new Coder((this.func != null) ? this.func.newHasher() : null);
}
public static final class Coder {
private final Hasher hasher;
private Coder(Hasher hasher) {
this.hasher = hasher;
if (this.hasher == null) LOG.warn("Hash实例为空 请检查对应HashFunction是否正确");
}
public Hasher hasher() {
return this.hasher;
}
/**
* HEX值 默认为小写
*
* @return
*/
public String hex() {
return this.hex(false);
}
/**
* HEX值 可指定大小写 此值返回了计算后的hash值 调用后若要再次计算附加部分 请重新生成实例再次进行计算
*
* @param upperCase
* @return
*/
public String hex(boolean upperCase) {
if (this.hasher == null) return null;
String hex = this.hasher.hash().toString();
if (upperCase) return hex.toUpperCase();
else return hex;
}
/**
* 获取值 此值返回了计算后的hash值 调用后若要再次计算附加部分 请重新生成实例再次进行计算
*
* @return
*/
public byte[] get() {
if (this.hasher == null) return null;
return this.hasher.hash().asBytes();
}
/**
* 仅用于类似于CRC32的机制 返回long
*
* @return
* @throws AppError
*/
public long getLong() throws AppError {
try {
return this.hasher.hash().asLong();
} catch (Throwable e) {
throw AppError.error("返回long值", e);
}
}
/**
* 操作数据
*
* @param bytes
* @return
*/
public Coder put(byte[] bytes) {
if (this.hasher == null) return this;
return this;
}
/**
* 操作字符串 默认UTF-8编码
*
* @param str
* @return
*/
public Coder put(String str) {
return put(str, Langs.CHARSET_UTF8);
}
/**
* 操作字符串 可设置字符集
*
* @param str
* @param charset
* @return
*/
public Coder put(String str, Charset charset) {
if (this.hasher == null) return this;
if (charset == null) charset = Langs.CHARSET_UTF8;
this.hasher.putString(str, charset);
return this;
}
/**
* 读取流内容 注意此处未对流进行回收关闭 需要在外部做try-catch-finally保护以回收流空间
*
* @param is
* @return
* @throws AppError
*/
public Coder put(InputStream is) throws AppError {
return this.put(is, BUF_SIZE);
}
/**
* 读取流内容 注意此处使用了function机制 使用 supplier 打开流对象 操作结束或异常时 自动关闭流资源
*
* @param sis
* @return
* @throws AppError
*/
public Coder put(Supplier sis) throws AppError {
return this.put(sis, BUF_SIZE);
}
/**
* 读取流内容 注意此处未对流进行回收关闭 需要在外部做try-catch-finally保护以回收流空间
*
* @param is
* @param bufSize
* @return
* @throws AppError
*/
public Coder put(InputStream is, int bufSize) throws AppError {
if (this.hasher == null || is == null) return this;
try {
int bufferLength = (bufSize > 0) ? bufSize : BUF_SIZE;
byte[] buffer = new byte[bufferLength];
int read = is.read(buffer, 0, bufferLength);
while (read > 0) {
this.hasher.putBytes(buffer, 0, read);
read = is.read(buffer, 0, bufferLength);
}
} catch (Throwable e) {
throw AppError.error("无法打开输入流" + e.getMessage(), e);
}
return this;
}
/**
* 读取流内容 注意此处使用了function机制 使用 supplier 打开流对象 操作结束或异常时 自动关闭流资源
*
* @param sis
* @param bufSize
* @return
* @throws AppError
*/
public Coder put(Supplier sis, int bufSize) throws AppError {
if (this.hasher == null || sis == null) return this;
InputStream is = null;
try {
is = sis.get();
return put(is, bufSize);
} catch (Throwable e) {
throw AppError.error("无法打开输入流" + e.getMessage(), e);
} finally {
close(is);
}
}
}
/**
* 基于 bcrypt 算法的密码验证机制
*/
public static final class BCryptCoder {
private BCryptCoder() {
}
public String salt() {
return BCrypt.gensalt();
}
public String salt(int log_rounds) {
return BCrypt.gensalt(log_rounds);
}
/**
* 加密密码,使用默认的 salt
*
* @param password
* @return
*/
public String hash(String password) {
return this.hash(password, 0);
}
/**
* 加密密码,使用给定 log_rounds 的 salt
*
* 其中默认 log_rounds <1 时使用 BCrypt.gensalt(10),否则按给定参数调用 BCrypt.gensalt(slatLogRound)
*
* slatLogRound 取值为 10、12 等
*
* @param password
* @param saltLogRound
* @return
*/
public String hash(String password, int saltLogRound) {
if (Strs.isEmpty(password)) return "";
return BCrypt.hashpw(password, (saltLogRound < 1) ? BCrypt.gensalt() : BCrypt.gensalt(saltLogRound));
}
public String hash(String password, String salt) {
if (Strs.isEmpty(password)) return "";
return BCrypt.hashpw(password, Strs.isEmpty(salt) ? salt() : salt);
}
/**
* 验证密码是否相符
*
* @param password 明文密码(待验证的密码)
* @param hashed 加密后的密码(原密码的hash加密结果)
* @return
*/
public boolean check(String password, String hashed) {
if (Strs.isEmpty(password)) return Strs.isEmpty(hashed);
else if (Strs.isEmpty(hashed)) return Strs.isEmpty(password);
return BCrypt.checkpw(password, hashed);
}
}
public static void close(Closeable target) {
try {
if (target != null) target.close();
} catch (Throwable ioe) {
// 忽略
}
}
}