都快3202年了,你还不会用Java生成计算机统一标识符

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,请自行配置。

写在最后

如果你有什么疑惑的话,可以在评论去留言。也欢迎各位大佬在评论区发表自己的看法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值