獲取伺服器 MAC 地址是軟體授權、節點識別、網路診斷的常見需求。傳統做法呼叫系統指令(ipconfig/ifconfig)再解析輸出,不僅平台相依、效能差,還需處理多語系輸出。JDK 1.4+ 引入 java.net.NetworkInterface,1.6 大幅增強功能,提供純 Java 跨平台解決方案。本文提供生產級工具類,含格式化、過濾虛擬/回環介面、多網卡支援。
| 方法 | 用途 |
|---|---|
getNetworkInterfaces() | 列舉所有網路介面 |
getHardwareAddress() | 取得 MAC (byte[]) |
isUp() | 介面是否啟用 |
isLoopback() | 是否回環介面 |
isVirtual() | 是否虛擬介面 (Docker、VPN 等) |
getMTU() | 最大傳輸單元 |
getDisplayName() | 友善名稱 (如 "Intel 82574L") |
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class MacAddressUtil {
public static List<String> getAllMacAddresses() {
List<String> macs = new ArrayList<>();
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
if (interfaces == null) return macs;
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (ni == null) continue;
// 過濾:啟用、非回環、非虛擬、有實體 MAC
if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) continue;
byte[] macBytes = ni.getHardwareAddress();
if (macBytes == null || macBytes.length != 6) continue;
macs.add(formatMac(macBytes));
}
} catch (SocketException e) {
// 記錄日誌,不拋出避免影響主流程
System.err.println("獲取 MAC 失敗: " + e.getMessage());
}
return macs;
}
public static String getPrimaryMacAddress() {
List<String> macs = getAllMacAddresses();
return macs.isEmpty() ? null : macs.get(0);
}
private static String formatMac(byte[] mac) {
StringBuilder sb = new StringBuilder(17);
for (int i = 0; i < mac.length; i++) {
if (i > 0) sb.append(':');
// 修正:byte 為有號,需 & 0xFF 轉無號再格式化
sb.append(String.format("%02X", mac[i] & 0xFF));
}
return sb.toString();
}
}
byte 為有號(-128~127),MAC 位元如 0xAB 會變成負數 -85。必須用 mac[i] & 0xFF 轉為無號整數再格式化,否則 Integer.toHexString 會產生 ffffffab。00:1A:2B:3C:4D:5E),符合 IEEE 802 標準,利於資料庫唯一索引與日誌檢索。isUp() && !isLoopback() && !isVirtual() 的介面;若需支援 Docker/K8s 環境下的宿主機識別,可保留虛擬介面並結合名稱前綴(eth、ens、bond)判斷。getNetworkInterfaces() 回傳順序不保證。可輔以 getMTU()(實體網卡通常 1500)、getDisplayName() 關鍵字排序,或讓使用者在管理介面確認選擇。// 呼叫
List<String> macs = MacAddressUtil.getAllMacAddresses();
macs.forEach(System.out::println);
// 典型輸出
00:1A:2B:3C:4D:5E // 實體網卡
00:50:56:C0:00:01 // VMware 虛擬網卡 (若未過濾 isVirtual)
00:50:56:C0:00:08
public boolean validateLicense(String licensedMac) {
String current = MacAddressUtil.getPrimaryMacAddress();
if (current == null) return false; // 無實體網卡環境(如純容器)需替代方案
return current.equalsIgnoreCase(licensedMac);
}
注意: 雲主機/容器環境 MAC 可能隨遷移變更,授權體系建議結合 CPUID、磁碟序號、雲廠商 Instance ID 多因子綁定。
| 現象 | 原因 | 解決 |
|---|---|---|
| 回傳空列表 | 無實體網卡、權限不足、JVM 安全管理員限制 | 以管理員/root 執行;檢查 SecurityManager 政策 |
| 獲取到 00:00:00:00:00:00 | 虛擬介面/隧道介面無實體 MAC | 加強 isVirtual()、macBytes.length == 6 過濾 |
| Windows 上顯示中文介面名 | getDisplayName() 回傳本地化名稱 | 僅作顯示用,邏輯判斷用 getName() (如 eth0、Ethernet0) |
採用 NetworkInterface 純 Java 方案,一次編寫、到處運行,徹底擺脫指令解析的平台相依與維護負擔。