Java生成计算机统一标识符
计算机统一标识符的概念
什么是计算机统一标识符?计算机统一标识符就相当于每台电脑每个系统的“身份证”。它是唯一的。通常,计算机统一标识符是根据电脑的硬件情况(主板、cpu的序列号,mac地址)和系统情况(windows/linux/unix)生成的。
Java语言的实现
下面这段代码浅浅的实现了计算机统一标识符
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>
* ComputerUniqueIdentificationUtil<br>
* 计算机唯一标识工具
* </p>
*
* @author Heping_Ge2333
* @version 2.0
* @since 2022/12/23
*/
@Slf4j
public class ComputerUniqueIdentificationUtil {
/**
* Windows OS Identification
*/
public static final String WINDOWS = "WINDOWS";
/**
* Linux OS Identification
*/
public static final String LINUX = "LINUX";
/**
* Unix OS Identification
*/
public static final String UNIX = "UNIX";
/**
* 正则表达式
*/
public static final String REGEX = "\\b\\w+:\\w+:\\w+:\\w+:\\w+:\\w+\\b";
private static final String WINDOWS_MOTHERBOARD_INFO_ERROR_MSG = "获取 Windows 主板信息错误";
private static final String WINDOWS_MAC_ADDRESS_ERROR_MSG = "获取 Windows MAC 信息错误";
private static final String WINDOWS_CPU_INFO_ERROR_MSG = "获取 Windows CPU 信息错误";
private static final String LINUX_MOTHERBOARD_INFO_ERROR_MSG = "获取 Linux 主板信息错误";
private static final String LINUX_MAC_ADDRESS_ERROR_MSG = "获取 Linux MAC 信息错误";
private static final String LINUX_CPU_INFO_ERROR_MSG = "获取 Linux CPU 信息错误";
private static final String DELETE_FILE_ERROR_MSG = "删除文件时发生了错误";
private ComputerUniqueIdentificationUtil() {
}
/**
* 在 Windows 环境下执行一个 vbs 脚本, 并返回运行结果
*
* @param result 用于存储返回结果的 StringBuilder
* @param file 存储 vbs 代码的文件
* @param fw 存储 vbs 代码文件的输入流
* @param vbs 要执行的代码
* @throws IOException 写入 vbs 脚本至文件 或 读取结果时发生 IO异常
* @author Heping_Ge2333
*/
private static void loadVBS(StringBuilder result, File file, FileWriter fw, String vbs) throws IOException {
fw.write(vbs);
fw.close();
Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
result.append(line);
}
input.close();
}
/**
* 获取 Windows 主板序列号
*
* @return String - 计算机主板序列号
* @author Heping_Ge2333
*/
private static String getWindowsMotherboardSerialNumber() {
StringBuilder result = new StringBuilder();
try {
File file = File.createTempFile("realhowto", ".vbs");
file.deleteOnExit();
FileWriter fw = new java.io.FileWriter(file);
String vbs = """
Set objWMIService = GetObject("winmgmts:\\\\.\\root\\cimv2")
Set colItems = objWMIService.ExecQuery _\s
("Select * from Win32_BaseBoard")\s
For Each objItem in colItems\s
Wscript.Echo objItem.SerialNumber\s
exit for ' do the first cpu only!\s
Next\s
""";
loadVBS(result, file, fw, vbs);
} catch (Exception e) {
log.error(WINDOWS_MOTHERBOARD_INFO_ERROR_MSG, e);
}
return result.toString().trim();
}
/**
* 获取 Linux 主板序列号
*
* @return String - 计算机主板序列号
* @author Heping_Ge2333
*/
private static String getLinuxMotherboardSerialNumber() {
String result = CommonConstants.EMPTY_STR;
String motherboardCmd = "dmidecode | grep 'Serial Number' | awk '{print $3}' | tail -1";
Process p;
try {
// 管道
p = Runtime.getRuntime().exec(new String[]{"sh", "-c", motherboardCmd});
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
result = br.readLine();
br.close();
} catch (IOException e) {
log.error(LINUX_MOTHERBOARD_INFO_ERROR_MSG, e);
}
return result;
}
/**
* 从字节获取 MAC 地址
*
* @param bytes - 字节
* @return String - MAC
* @author Heping_Ge2333
*/
private static String getMacAddressFromBytes(byte[] bytes) {
StringBuilder mac = new StringBuilder();
byte currentByte;
boolean first = false;
for (byte b : bytes) {
if (first) {
mac.append("-");
}
currentByte = (byte) ((b & 240) >> 4);
mac.append(String.format("%02X", currentByte));
currentByte = (byte) (b & 15);
mac.append(String.format("%02X", currentByte));
first = true;
}
return mac.toString().toUpperCase();
}
/**
* 获取 Windows 网卡的 MAC 地址
*
* @return String - MAC 地址
* @author Heping_Ge2333
*/
private static String getWindowsMACAddress() {
InetAddress ip;
NetworkInterface ni;
List<String> macList = new ArrayList<>();
try {
Enumeration<NetworkInterface> netInterfaces = NetworkInterface
.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
ni = netInterfaces.nextElement();
// 遍历所有 IP 特定情况,可以考虑用 ni.getName() 判断
Enumeration<InetAddress> ips = ni.getInetAddresses();
while (ips.hasMoreElements()) {
ip = ips.nextElement();
// 非127.0.0.1
if (!ip.isLoopbackAddress() && ip.getHostAddress().matches("(\\d{1,3}\\.){3}\\d{1,3}")) {
macList.add(getMacAddressFromBytes(ni.getHardwareAddress()));
}
}
}
} catch (Exception e) {
log.error(WINDOWS_MAC_ADDRESS_ERROR_MSG, e);
}
if (!macList.isEmpty()) {
return macList.get(0);
} else {
return "";
}
}
/**
* 获取 Linux 网卡的 MAC 地址 (如果 Linux 下有 eth0 这个网卡)
*
* @return String - MAC 地址
* @author Heping_Ge2333
*/
private static String getLinuxMACAddressForEth0() {
String mac = null;
BufferedReader bufferedReader = null;
Process process;
try {
// Linux下的命令,一般取eth0作为本地主网卡
process = Runtime.getRuntime().exec("ipconfig eth0");
// 显示信息中包含有 MAC 地址信息
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
int index;
while ((line = bufferedReader.readLine()) != null) {
// 寻找标示字符串[hwaddr]
index = line.toLowerCase().indexOf("hwaddr");
if (index >= 0) {
// // 找到并取出 MAC 地址并去除2边空格
mac = line.substring(index + "hwaddr".length() + 1).trim();
break;
}
}
} catch (IOException e) {
log.error(LINUX_MAC_ADDRESS_ERROR_MSG, e);
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e1) {
log.error(LINUX_MAC_ADDRESS_ERROR_MSG, e1);
}
}
return mac;
}
/**
* 获取 Linux 网卡的 MAC 地址
*
* @return String - MAC 地址
* @author Heping_Ge2333
*/
private static String getLinuxMACAddress() {
String mac = null;
BufferedReader bufferedReader = null;
Process process;
try {
// Linux下的命令 显示或设置网络设备
process = Runtime.getRuntime().exec("ipconfig");
// 显示信息中包含有 MAC 地址信息
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
Pattern pat = Pattern.compile(REGEX);
Matcher mat = pat.matcher(line);
if (mat.find()) {
mac = mat.group(0);
}
}
} catch (IOException e) {
log.error(LINUX_MAC_ADDRESS_ERROR_MSG, e);
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e1) {
log.error(LINUX_MAC_ADDRESS_ERROR_MSG, e1);
}
}
return mac;
}
/**
* 获取 Windows 的 CPU 序列号
*
* @return String - CPU 序列号
* @author Heping_Ge2333
*/
private static String getWindowsProcessorSerialNumber() {
StringBuilder result = new StringBuilder();
try {
File file = File.createTempFile("tmp", ".vbs");
file.deleteOnExit();
FileWriter fw = new java.io.FileWriter(file);
String vbs = """
Set objWMIService = GetObject("winmgmts:\\\\.\\root\\cimv2")
Set colItems = objWMIService.ExecQuery _\s
("Select * from Win32_Processor")\s
For Each objItem in colItems\s
Wscript.Echo objItem.ProcessorId\s
exit for ' do the first cpu only!\s
Next\s
""";
loadVBS(result, file, fw, vbs);
if (!file.delete()) {
log.error(DELETE_FILE_ERROR_MSG);
}
} catch (Exception e) {
log.error(WINDOWS_CPU_INFO_ERROR_MSG, e);
}
return result.toString().trim();
}
/**
* 获取 Linux 的 CPU 序列号
*
* @return String - CPU 序列号
* @author Heping_Ge2333
*/
private static String getLinuxProcessorSerialNumber() {
String result = "";
String CPU_ID_CMD = "dmidecode";
BufferedReader bufferedReader;
Process p;
try {
// 管道
p = Runtime.getRuntime().exec(new String[]{"sh", "-c", CPU_ID_CMD});
bufferedReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
int index;
while ((line = bufferedReader.readLine()) != null) {
index = line.toLowerCase().indexOf("uuid");
if (index >= 0) {
result = line.substring(index + "uuid".length() + 1).trim();
break;
}
}
} catch (IOException e) {
log.error(LINUX_CPU_INFO_ERROR_MSG, e);
}
return result.trim();
}
/**
* 获取当前计算机操作系统名称 例如:Windows,Linux,Unix等.
*
* @return String - 计算机操作系统名称
* @author Heping_Ge2333
*/
public static String getOSName() {
return System.getProperty("os.name").toUpperCase();
}
/**
* 获取当前计算机操作系统名称前缀 例如:Windows, Linux, Unix等.
*
* @return String - 计算机操作系统名称
* @author Heping_Ge2333
*/
public static String getOSNamePrefix() {
String name = getOSName();
if (name.startsWith(WINDOWS)) {
return WINDOWS;
} else if (name.startsWith(LINUX)) {
return LINUX;
} else if (name.startsWith(UNIX)) {
return UNIX;
} else {
return CommonConstants.EMPTY_STR;
}
}
/**
* 获取当前计算机主板序列号
*
* @return String - 计算机主板序列号
* @author Heping_Ge2333
*/
public static String getMotherBoardSerialNumber() {
return switch (getOSNamePrefix()) {
case WINDOWS -> getWindowsMotherboardSerialNumber();
case LINUX -> getLinuxMotherboardSerialNumber();
default -> CommonConstants.EMPTY_STR;
};
}
/**
* 获取当前计算机网卡的 MAC 地址
*
* @return String - 网卡的 MAC 地址
* @author Heping_Ge2333
*/
public static String getMACAddress() {
switch (getOSNamePrefix()) {
case WINDOWS -> {
return getWindowsMACAddress();
}
case LINUX -> {
String macAddressForEth0 = getLinuxMACAddressForEth0();
if (StringUtil.isEmpty(macAddressForEth0)) {
macAddressForEth0 = getLinuxMACAddress();
}
return macAddressForEth0;
}
default -> {
return CommonConstants.EMPTY_STR;
}
}
}
/**
* 获取当前计算机的 CPU 序列号
*
* @return String - CPU 序列号
* @author Heping_Ge2333
*/
public static String getCPUSerialNumber() {
return switch (getOSNamePrefix()) {
case WINDOWS -> getWindowsProcessorSerialNumber();
case LINUX -> getLinuxProcessorSerialNumber();
default -> CommonConstants.EMPTY_STR;
};
}
/**
* 获取计算机唯一标识
*
* @return ComputerUniqueIdentification - 计算机唯一标识
* @author Heping_Ge2333
*/
public static ComputerUniqueIdentification getComputerUniqueIdentification() {
return new ComputerUniqueIdentification(getOSNamePrefix(), getMotherBoardSerialNumber(), getMACAddress(), getCPUSerialNumber());
}
/**
* 获取计算机唯一标识的 json 格式文本
*
* @return String - 计算机唯一标识
* @author Heping_Ge2333
*/
public static String getComputerUniqueIdentificationString() {
return getComputerUniqueIdentification().toString();
}
/**
* 获取计算机唯一标识的SHA-512哈希值
*
* @return 计算机唯一标识的SHA-512哈希值
*/
public static String getComputerUniqueIdentificationHash() {
return HashUtil.sha512(getComputerUniqueIdentification());
}
/**
* <p>
* ComputerUniqueIdentification<br>
* 计算机唯一标识
* </p>
*
* @author Heping_Ge2333
* @version 2.0
* @since 2022/12/13
*/
@Data
private static class ComputerUniqueIdentification {
private final String osNamePrefix;
private final String motherboardSerialNumber;
private final String macAddress;
private final String cpuSerialNumber;
public ComputerUniqueIdentification(String osNamePrefix, String motherboardSerialNumber, String macAddress, String cpuSerialNumber) {
this.osNamePrefix = osNamePrefix;
this.motherboardSerialNumber = motherboardSerialNumber;
this.macAddress = macAddress;
this.cpuSerialNumber = cpuSerialNumber;
}
/**
* 将计算机唯一标识转化为 json 并返回
*
* @return 转化后的结果
*/
@Override
public String toString() {
return '{' +
"\n\t\"osNamePrefix\": \"" + osNamePrefix + "\"," +
"\n\t\"motherboardSerialNumber\": \"" + motherboardSerialNumber + "\"," +
"\n\t\"macAddress\": \"" + macAddress + "\"," +
"\n\t\"cpuSerialNumber\": \"" + cpuSerialNumber + "\"" +
"\n}";
}
}
}
这是项目里还有用到的几个工具类:
CommonConstants.java
//CommonConstants.java
/**
* <p>
* CommonConstants<br>
* 常用常量集合
* </p>
*
* @author Heping_Ge2333
* @version 1.0
* @since 2022/12/23
*/
public class CommonConstants {
/**
* 空字符串
*/
public static final String EMPTY_STR = "";
private CommonConstants() {
}
}
ConvertUtil.java:
//ConvertUtil.java
import java.math.BigInteger;
/**
* <p>
* ConvertUtil<br>
* 转换工具
* </p>
*
* @author Heping_Ge2333
* @version 1.0
* @since 2022/12/23
*/
public class ConvertUtil {
private ConvertUtil() {
}
/**
* 将字节数组转换为字符串
*
* @param bytes 待转换的字节数组
* @return 转换成的字符串
*/
public static String convertBytesToString(byte[] bytes) {
BigInteger no = new BigInteger(1, bytes);
// Convert message digest into hex value
String text = no.toString(16);
while (text.length() < 32) {
text = "0" + text;
}
return text;
}
}
HashUtil.java:
//HashUtil.java
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* <p>
* HashUtil<br>
* 哈希算法工具
* </p>
*
* @author Heping_Ge2333
* @version 1.0
* @since 2022/12/23
*/
@Slf4j
public class HashUtil {
/**
* MD5
*/
public static final String MD5 = "MD5";
/**
* SHA-256
*/
public static final String SHA_256 = "SHA-256";
/**
* SHA-512
*/
public static final String SHA_512 = "SHA-512";
private static final String NO_SUCH_ALGO_ERROR_MSG = "找不到指定的算法";
private HashUtil() {
}
/**
* 获取对象的哈希值
*
* @param algo 算法名称
* @param msg 原字符串
* @return 字节数组形式的哈希值
*/
public static byte[] getHash(String algo, String msg) {
try {
MessageDigest md = MessageDigest.getInstance(algo);
md.update(msg.getBytes(StandardCharsets.UTF_8));
byte[] bytes = md.digest();
return bytes;
} catch (NoSuchAlgorithmException e) {
log.error(NO_SUCH_ALGO_ERROR_MSG + " " + algo + " !", e);
return null;
}
}
/**
* 获取对象的MD5哈希值
*
* @param obj 原对象
* @return 原对象的MD5值
*/
public static String md5(Object obj) {
return ConvertUtil.convertBytesToString(getHash(MD5, obj.toString()));
}
/**
* 获取对象的MD5哈希值
*
* @param obj 原对象
* @return 原对象的MD5值
*/
public static String sha256(Object obj) {
return ConvertUtil.convertBytesToString(getHash(SHA_256, obj.toString()));
}
/**
* 获取对象的MD5哈希值
*
* @param obj 原对象
* @return 原对象的MD5值
*/
public static String sha512(Object obj) {
return ConvertUtil.convertBytesToString(getHash(SHA_512, obj.toString()));
}
}
StringUtil.java:
//StringUtil.java
/**
* <p>
* StringUtil<br>
* 字符串工具
* </p>
*
* @author Heping_Ge2333
* @version 1.0
* @since 2022/12/23
*/
public class StringUtil {
private StringUtil() {
}
/**
* 判断字符串是否为空字符串
*
* @param str 要判断的字符串
* @return 是否为空
*/
public static boolean isEmpty(String str) {
if (str == null) return true;
return str.replaceAll("\\s", "").equals(CommonConstants.EMPTY_STR);
}
}
注意:本项目中使用了lombok和Slf4j,请自行配置。
写在最后
如果你有什么疑惑的话,可以在评论去留言。也欢迎各位大佬在评论区发表自己的看法。