獲取客戶端Mac地址


近期有個需求,需要獲取客戶端Mac地址作為白名單驗證的依據。使用.net,B/S架構。先百度找了一些獲取mac地址的方法,

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
using System.Configuration;
using System.Web;
using System.Security.Cryptography;
using System.Data;
using System.Web.UI.WebControls;
using System.Management;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;

namespace Qianxun.Common
{
    public class MacAddress
    {
        #region 獲取mac地址的一些方法
        #region 1 通過IPConfig命令讀取MAC地址(選用)
        /// <summary>
        /// 根據截取ipconfig /all命令的輸出流獲取網卡Mac
        /// </summary>
        /// <returns></returns>
        public static List<string> GetMacByIPConfig()
        {
            List<string> macs = new List<string>();
            try
            {
                ProcessStartInfo startInfo = new ProcessStartInfo("ipconfig", "/all");
                startInfo.UseShellExecute = false;
                startInfo.RedirectStandardInput = true;
                startInfo.RedirectStandardOutput = true;
                startInfo.RedirectStandardError = true;
                startInfo.CreateNoWindow = true;
                Process p = Process.Start(startInfo);
                // 截取輸出流
                StreamReader reader = p.StandardOutput;
                string line = reader.ReadLine();

                while (!reader.EndOfStream)
                {
                    if (!string.IsNullOrEmpty(line))
                    {
                        line = line.Trim();

                        if (line.StartsWith("Physical Address") || line.StartsWith("物理地址"))
                        {
                            macs.Add(line.Substring(line.IndexOf(":") + 1, line.Length - line.IndexOf(":") - 1));
                            break;
                        }
                    }

                    line = reader.ReadLine();
                }

                // 等待程序執行完退出進程
                p.WaitForExit();
                p.Close();
                reader.Close();
            }
            catch (Exception ex) { 
            
            }
            return macs;
        }
        #endregion

        #region 2 通過WMI讀取MAC地址
        /// <summary>
        /// 通過WMI讀取系統信息里的網卡MAC
        /// 該方法依賴WMI的系統服務,該服務一般不會被關閉;但如果系統服務缺失或者出現問題,該方法無法取得MAC地址。
        /// </summary>
        /// <returns></returns>
        public static List<string> GetMacByWMI()
        {
            List<string> macs = new List<string>();
            try
            {
                string mac = "";
                ManagementClass mc = new ManagementClass(" Win32_NetworkAdapterConfiguration ");
                ManagementObjectCollection moc = mc.GetInstances();
                foreach (ManagementObject mo in moc)
                {
                    if ((bool)mo[" IPEnabled "])
                    {
                        mac = mo[" MacAddress "].ToString();
                        macs.Add(mac);
                    }
                }
                moc = null;
                mc = null;
            }
            catch(Exception ex)
            {
            }

            return macs;
        }
        #endregion

        #region 3 通過NetworkInterface讀取MAC地址:1)如果當前的網卡是禁用狀態(硬件處於硬關閉狀態),取不到該網卡的MAC地址,(您可以通過禁用網卡進行試驗)。2)如果當前啟用了多個網卡,最先返回的地址是最近啟用的網絡連接的信息
        // 返回描述本地計算機上的網絡接口的對象(網絡接口也稱為網絡適配器)。
        public static NetworkInterface[] NetCardInfo()
        {
            return NetworkInterface.GetAllNetworkInterfaces();
        }

        /// <summary>
        /// 通過NetworkInterface讀取網卡Mac
        /// </summary>
        /// <returns></returns>
        public static List<string> GetMacByNetworkInterface()
        {
            List<string> macs = new List<string>();
            try
            {
                NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
                foreach (NetworkInterface ni in interfaces)
                {
                    macs.Add(ni.GetPhysicalAddress().ToString());
                }
            }
            catch (Exception ex)
            { 
            
            }
            return macs;
        }
        #endregion
        #region 4 通過SendARP讀取MAC地址
        /// <summary>
        /// 通過SendARP獲取網卡Mac
        /// 網絡被禁用或未接入網絡(如沒插網線)時此方法失靈
        /// </summary>
        /// <param name="remoteIP"></param>
        /// <returns></returns>
        public static string GetMacBySendARP(string remoteIP)
        {
            StringBuilder macAddress = new StringBuilder();

            try
            {
                Int32 remote = inet_addr(remoteIP);

                Int64 macInfo = new Int64();
                Int32 length = 6;
                SendARP(remote, 0, ref macInfo, ref length);

                string temp = Convert.ToString(macInfo, 16).PadLeft(12, '0').ToUpper();

                int x = 12;
                for (int i = 0; i < 6; i++)
                {
                    if (i == 5)
                    {
                        macAddress.Append(temp.Substring(x - 2, 2));
                    }
                    else
                    {
                        macAddress.Append(temp.Substring(x - 2, 2) + " - ");
                    }
                    x -= 2;
                }

                return macAddress.ToString();
            }
            catch
            {
                return macAddress.ToString();
            }
        }

        [DllImport(" Iphlpapi.dll ")]
        private static extern int SendARP(Int32 dest, Int32 host, ref Int64 mac, ref Int32 length);
        [DllImport(" Ws2_32.dll ")]
        private static extern Int32 inet_addr(string ip);
        #endregion

        #region 5 從注冊表讀取MAC地址
        //常規用戶可通過讀取注冊表項Windows Genuine Advantage獲取到物理網卡地址。
        //1)如果注冊表項被修改,則無法取得該MAC地址
        //HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Genuine Advantage
        #endregion
        #endregion
    }
}
View Code

測試時猛然發現多個客戶端獲取到的mac地址都是一樣的,驚覺獲取的是服務端的Mac地址,服務端語言獲取服務端Mac,那么我需要獲取客戶端Mac,應該使用客戶端語言,最先想到的是js(因為有碰到過客戶端ip和服務端ip的問題,這里倒也能想清楚)。百度了一圈,mac地址需要ActiveX腳本支持,

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ie瀏覽器獲取客戶端Mac地址</title>
</head>

<body>

    <object classid="CLSID:76A64158-CB41-11D1-8B02-00600806D9B6" id="locator" style="display: none; visibility: hidden"></object>
    <object classid="CLSID:75718C9A-F029-11d1-A1AC-00C04FB6C223" id="foo" style="display: none; visibility: hidden"></object>
    <form name="myForm">
        <br />
        MAC地址:<input type="text" name="macAddress">
    </form>
</body>
</html>

<script language="javascript">
    var sMacAddr = "";
    var sIPAddr = "";
    var sDNSName = "";
    var service = locator.ConnectServer();
    service.Security_.ImpersonationLevel = 3;
    service.InstancesOfAsync(foo, 'Win32_NetworkAdapterConfiguration');
</script>
<script for="foo" event="OnObjectReady(objObject,objAsyncContext)" language="JScript">
    if (objObject.IPEnabled != null && objObject.IPEnabled != "undefined" && objObject.IPEnabled == true) {
        if (objObject.IPEnabled && objObject.IPAddress(0) != null && objObject.IPAddress(0) != "undefined")
            sIPAddr = objObject.IPAddress(0);
        if (objObject.MACAddress != null && objObject.MACAddress != "undefined")
            sMacAddr = objObject.MACAddress;
        if (objObject.DNSHostName != null && objObject.DNSHostName != "undefined")
            sDNSName = objObject.DNSHostName;
    }
</script>

<script for="foo" event="OnCompleted(hResult,pErrorObject, pAsyncContext)" language="JScript">
    myForm.macAddress.value = sMacAddr;
</script>

<script>
    //判斷瀏覽器是否支持ActiveX控件
    //if (window.ActiveXObject) {
    //支持-通過ActiveXObject的一個新實例來創建XMLHttpRequest對象
    var WshShell = new ActiveXObject("WScript.Shell");
    //}
    //else {
    //    $("#tips").html("請使用ie並將本系統域名加入受信任的站點,並在internet選項-受信任的站點中開啟ActiveX插件和腳本的相關權限");
    //}
</script>
View Code

而這個東西僅支持ie,但是客戶系統對ie兼容性不好,如果要調整,工作量巨大,那么就只能讓其他瀏覽器比如chorme去兼容ActiveX。

1.首先找到chorme for ActiveX擴展插件,試了好幾個版本,也依然無法獲取到mac地址,不知道是我的使用方式有問題,還是這個方法已經失效。(chorme已經升級到當前最新版本 96.0.4664.45(正式版本) (64 位))。

2.之后找到IE Tab擴展插件,但是好像僅僅是在chorme上嵌入了一個ie內核,這樣系統同樣是不兼容的。

3.然后找到了油猴,想着寫一個腳本獲取mac地址存入c盤的一個txt文本,然后系統去讀取這個txt文件即可。奈何油猴寫了個alert(‘hello world‘)也沒生效,不知道是使用方法有誤,還是相關機制阻止了這種毫無意義的打擾。想到另一個問題,如果是后端去保存文件,其實是保存到服務器,而后端讀取文件也需要客戶端進行上傳。而客戶端腳本是沒有權限去操作文件直接保存到txt(有點木馬的特性,奈何沒有涉及到這一塊的技術)。所以該方案暫時擱置。

4.又找到一個插件PluginOK,但是是付費插件,原理應該是js調用c++去獲取計算機物理信息,然后通過雙向通信集成到系統。感覺有些大材小用,也沒有這么多預算,暫時擱置,也沒實測。這一痛點,成就了一家企業。

提取問題的根本矛盾點:mac地址僅支持ie獲取,系統不兼容ie,那么就只能分成兩步,1.ie獲取mac 2.使用chorme登錄系統操作,這1和2之間需要產生一個聯系來把1獲取的數據傳遞給2,想到上次微信掃碼登錄的邏輯,使用了預登陸機制,聯想到暴雪的安全令機制,有了些許思路:

1.login頁面創建一條預登陸記錄存入數據庫,且獲取到id,在頁面上拼接成安全令登錄url

2.用戶使用ie登錄前一步拼接而成的url獲取到mac地址且根據入參id存入到對應的數據庫記錄中(用戶的ie需要先進行配置:1.ie瀏覽器internet選項-安全-受信任的站點將系統域名加入受信任的站點 2.還是剛才的地方 自定義級別 將ActiveX控件和插件相關權限打開

3.返回原來的login頁面輸入賬號密碼即可登錄,正常邏輯登錄之后,配合預登陸id(可以從數據庫獲取mac地址)可以進行mac地址白名單的匹配

曲線救國,安全令增加一些交互效果,各處增加操作詳細說明,倒也算勉強完成了需求


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM