如何在Java中获取唯一的计算机标识符(例如磁盘ID或主板ID)?

69

我想使用Java在Windows、MacOS和可能的Linux上获取与计算机唯一相关的id。可以是磁盘UUID、主板序列号等。

Runtime.getRuntime().exec可以被使用(它不是一个applet)。

有什么想法吗?


1
这是为了反盗版计划吗? - Mark Byers
3
可能会是一个JNI调用,没有本地Java代码。 - curtisk
1
是的,这是用于类似反盗版方案的东西,用于识别计算机。 - Gohu
6
只要可以通过exec()运行的东西都可以被替换。用户只需要创建一个程序,始终返回预期结果即可。这只是从工作机器中获取数据并编写输出相同结果的程序(例如从文件中读取)的问题。 - Peter Lawrey
11个回答

54
问题在于MAC地址,因为计算机可以连接多个网络适配器。现在大部分都有两个(无线+电缆)。在这种情况下,人们需要知道应该使用哪个适配器的MAC地址。我在我的系统上测试了MAC解决方案,但是我有4个适配器(电缆,WiFi,Virtual Box的TAP适配器和一个蓝牙适配器),我不知道该选择哪个MAC地址......如果要使用当前正在使用的适配器(已分配地址),则会出现新问题,因为有人可能会拿起自己的笔记本电脑并从电缆适配器切换到WiFi。在这种情况下,当笔记本电脑通过电缆连接时存储的MAC地址现在将无效。
例如,这些是我在系统中找到的适配器:
lo MS TCP Loopback interface
eth0 Intel(R) Centrino(R) Advanced-N 6205
eth1 Intel(R) 82579LM Gigabit Network Connection
eth2 VirtualBox Host-Only Ethernet Adapter
eth3 Sterownik serwera dostepu do sieci LAN Bluetooth

我用来列出它们的代码:

Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
    NetworkInterface ni = nis.nextElement();
    System.out.println(ni.getName() + " " + ni.getDisplayName());
}

在这个页面上提供的选项中,我认为最可接受的选择,也是我在解决方案中使用的选择是@Ozhan Duz提供的那个,另一个与@finnw答案类似的选择是使用JACOB,值得一提的是com4j - 示例可以使用WMI,可在此处找到:

ISWbemLocator wbemLocator = ClassFactory.createSWbemLocator();
ISWbemServices wbemServices = wbemLocator.connectServer("localhost","Root\\CIMv2","","","","",0,null);
ISWbemObjectSet result = wbemServices.execQuery("Select * from Win32_SystemEnclosure","WQL",16,null);
for(Com4jObject obj : result) {
    ISWbemObject wo = obj.queryInterface(ISWbemObject.class);
    System.out.println(wo.getObjectText_(0));
}

这将打印一些计算机信息以及计算机序列号。请注意,此示例所需的所有类都必须由maven-com4j-plugin生成。maven-com4j-plugin的示例配置如下:
<plugin>
    <groupId>org.jvnet.com4j</groupId>
    <artifactId>maven-com4j-plugin</artifactId>
    <version>1.0</version>
    <configuration>
        <libId>565783C6-CB41-11D1-8B02-00600806D9B6</libId>
        <package>win.wmi</package>
        <outputDirectory>${project.build.directory}/generated-sources/com4j</outputDirectory>
    </configuration>
    <executions>
        <execution>
            <id>generate-wmi-bridge</id>
            <goals>
                <goal>gen</goal>
            </goals>
        </execution>
    </executions>
</plugin>

以上的配置将告诉插件在项目文件夹中的target/generated-sources/com4j目录中生成类。
对于那些想要看到即用解决方案的人,我包含了三个类的链接,这些类可以在Windows、Linux和Mac OS上获取机器SN:

1
在Windows Nano Server 2016上,格式为“SerialNumber = xxx”。示例代码将返回每个“=”作为序列号。在其他Windows安装中,它对我有效。 - Horcrux7

37

OSHI项目提供了独立于平台的硬件工具。

Maven依赖:

<dependency>
    <groupId>com.github.oshi</groupId>
    <artifactId>oshi-core</artifactId>
    <version>LATEST</version>
</dependency>

例如,您可以使用以下代码来唯一标识机器:
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.ComputerSystem;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;

class ComputerIdentifier
{
    static String generateLicenseKey()
    {
        SystemInfo systemInfo = new SystemInfo();
        OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
        HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
        CentralProcessor centralProcessor = hardwareAbstractionLayer.getProcessor();
        ComputerSystem computerSystem = hardwareAbstractionLayer.getComputerSystem();

        String vendor = operatingSystem.getManufacturer();
        String processorSerialNumber = computerSystem.getSerialNumber();
        String processorIdentifier = centralProcessor.getIdentifier();
        int processors = centralProcessor.getLogicalProcessorCount();

        String delimiter = "#";

        return vendor +
                delimiter +
                processorSerialNumber +
                delimiter +
                processorIdentifier +
                delimiter +
                processors;
    }

    public static void main(String[] arguments)
    {
        String identifier = generateLicenseKey();
        System.out.println(identifier);
    }
}

我的机器的输出结果:

Microsoft#57YRD12#Intel64 Family 6 Model 60 Stepping 3#8

您的输出将不同,因为至少处理器序列号将不同。


3
请注意,根据您的操作系统,其中一些操作需要 root 访问权限。这可能是一个限制因素。 - user489041
@user489041 针对哪些操作系统进行什么样的操作? - Rafat Rifaie

23

通常使用MAC地址与网络卡相关联。

可以通过以下API在Java 6中获取该地址:

获取硬件地址的Java 6文档

虽然我没有在Java中使用过它,但对于其他网络识别应用程序,它很有帮助。


6
我已经考虑过了,但是当网络卡未连接时,我无法获取任何MAC地址。 - Gohu
用户也可以切换网络卡。在某些笔记本电脑上,当使用电池时,有线以太网卡会被禁用以节省电池电量,因此操作系统无法看到它。 - Steve Kuo
3
请注意,甚至不需要更换网络适配器就可以欺骗MAC地址:http://www.aboutlinux.info/2005/09/how-to-change-mac-address-of-your.html - Alex Fedulov
这对我来说不太可行,因为它需要在Linux上获取root权限。 - dessalines
我曾尝试在虚拟机上使用MAC地址,但发现它并不够稳定。除非您能保证在虚拟机移动和重启时MAC地址的稳定性,否则将MAC地址用作机器标识符会导致问题。请参阅Bartosz Fiyrn下面的答案以获得更好的方法。 - David

12

你想用这个唯一标识符做什么?也许你可以不用这个ID就实现你想要的功能。

MAC地址可能是一个选项,但这并不是一个可信的唯一标识符,因为用户可以更改计算机的MAC地址。

要获取主板或处理器ID,请查看此链接


9

仅在Windows系统中,您可以使用WMI通过COM桥接(例如JACOB)获取主板ID。

示例:

import java.util.Enumeration;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.EnumVariant;
import com.jacob.com.Variant;

public class Test {
    public static void main(String[] args) {
        ComThread.InitMTA();
        try {
            ActiveXComponent wmi = new ActiveXComponent("winmgmts:\\\\.");
            Variant instances = wmi.invoke("InstancesOf", "Win32_BaseBoard");
            Enumeration<Variant> en = new EnumVariant(instances.getDispatch());
            while (en.hasMoreElements())
            {
                ActiveXComponent bb = new ActiveXComponent(en.nextElement().getDispatch());
                System.out.println(bb.getPropertyAsString("SerialNumber"));
                break;
            }
        } finally {
            ComThread.Release();
        }
    }
}

如果您选择使用MAC地址来识别机器,您可以使用WMI来确定接口是否通过USB连接(如果您想排除USB适配器)。通过WMI获取硬盘ID也是可能的,但不可靠。

9

不清楚您所有的要求。例如,您是想从全世界所有计算机中唯一地识别一台计算机,还是只是想从应用程序用户集合中唯一地识别一台计算机。此外,您能在系统上创建文件吗?

如果您能够创建文件,则可以创建一个文件,并使用文件的创建时间作为唯一标识。如果在用户空间中创建,则它将唯一地识别特定机器上应用程序的用户。如果在全局某处创建,则可以唯一地识别该计算机。

同样,就像大多数事情一样,“快”到什么程度才算足够快...或者在这种情况下,“独特”到什么程度才算足够独特。


8
请注意在使用MAC地址作为标识符时要小心。我遇到过几个问题:
  1. 在OS X上,未激活/启用的以太网端口不会显示在NetworkInterface.getNetworkInterfaces()枚举中。
  2. 如果您拥有适当的操作系统权限,则轻松更改卡上的MAC地址。
  3. Java有一种不正确地识别“虚拟”接口的习惯。即使使用NetworkInterface.isVirtual()也不能总是告诉你真相。
即使存在以上问题,我仍然认为这是最好的纯Java方法来锁定许可证的硬件。

2

1
你放在CPUID/ Moboserial后面的链接描述了Windows特定的方法。这不是跨平台的。 - BalusC
1
在Linux中,您可以使用以下命令获取硬盘序列号:hdparm -i /dev/sda1 | awk '/SerialNo=/{print $NF}'(只需识别操作系统并尝试不同的方法)。您可以使用lshw命令查找主板序列号。 - Michel Gokan Khan
4
不要指望防病毒软件允许出现在您硬盘上的奇怪vbs脚本。大多数情况下,它们会立即阻止该文件,在您有机会执行它之前。 - Alex Fedulov

1

为了唯一地识别Windows机器。 使用wmic时,请确保有备选方法的策略。由于“wmic bios get serialnumber”可能无法在所有计算机上工作,您可能需要使用其他方法:

# Get serial number from bios
wmic bios get serialnumber
# If previous fails, get UUID
wmic csproduct get UUID
# If previous fails, get diskdrive serialnumber
wmic DISKDRIVE get SerialNumber

资源: 唯一标识Windows机器的最佳方法 http://www.nextofwindows.com/the-best-way-to-uniquely-identify-a-windows-machine/


1

如果任务是记录系统的唯一标识符,使用MAC id是最简单的方法。

虽然更改mac id是可能的,但只有在替换相应设备时才能更改系统的其他id。

因此,除非我们知道需要唯一标识符的目的,否则我们可能无法找到合适的解决方案。

然而,下面的链接有助于提取mac地址。 http://www.stratos.me/2008/07/find-mac-address-using-java/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接