两年前做的一个项目,当时客户的需求还没有完成,客户就提出了解决安全的问题,这是当时写的一个记录,分享到这里。


解决安全登录的问题

  去给客户演示系统,演示完刚开发完的系统以后客户就将我们的系统批的遍体鳞伤,这时才知道自己的系统比想象中的更不堪(不是我等无能,是各种原因全都赶上了)。在如此不堪的系统面前,客户又提出了一个需求,要限制用户的登录机器。补充一下,演示的系统是一个 ERP 系统,是 BS 结构的,后端用 Java 写的,项目是部署在阿里云上的,客户的每个门店都可以访问。但是,客户要求,要限制能够登录系统的电脑,客户明确要求需要绑定 MAC 地址。因为系统里的数据比较重要,不能让员工回家登录系统,因此必须要进行限制。


解决思路

  这样的问题,能想到的解决思路只有两个:(当时的思路,其实思路远不止这些)

  1、在 EXE 文件中嵌入一个浏览器控件,浏览器控件中显示 ERP 的页面,EXE 获取 MAC 地址后提交到服务器。感觉这样先是要处理 EXE 提交的 MAC 地址,然后还要和页面交互,想想貌似比较复杂,就否掉了。

  2、写一个 OCX,让页面中的 JS 与 OCX 进行交互,OCX 获取到 MAC 地址后,将 MAC 返回给 JS,JS 通过 DOM 操作写入到对应的表单中,然后和用户名、密码一起提交给服务器。感觉这个好像实现起来还比较简单。就这个吧!


OCX 中获取 MAC 地址的关键代码

  OCX 中可以直接调用 Windows 操作系统的 API 函数,写起来也比较简单,代码如下:

BSTR CGetMacCtrl::GetMacAddress(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());


CString strResult;


// TODO: 在此添加调度处理程序代码
ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES);
PIP_ADAPTER_ADDRESSES pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
if (pAddresses == NULL)
{
return NULL;
}

if(GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW)
{
free(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
if (pAddresses == NULL)
{
return NULL;
}
}


wchar_t acMAC[32] = { 0 };


if(GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == NO_ERROR)
{
for(PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses; pCurrAddresses != NULL; pCurrAddresses = pCurrAddresses->Next)
{
// 确保 MAC 地址的长度为 00-00-00-00-00-00
if(pCurrAddresses->PhysicalAddressLength != 6)
{
continue;
}
wsprintf((LPWSTR)acMAC, _T("%02X-%02X-%02X-%02X-%02X-%02X"),
int (pCurrAddresses->PhysicalAddress[0]),
int (pCurrAddresses->PhysicalAddress[1]),
int (pCurrAddresses->PhysicalAddress[2]),
int (pCurrAddresses->PhysicalAddress[3]),
int (pCurrAddresses->PhysicalAddress[4]),
int (pCurrAddresses->PhysicalAddress[5]));
break;
}
}


free(pAddresses);
strResult = acMAC;


return strResult.AllocSysString();
}

  代码差不多就这样吧,我用的 VS2012 写的 ActiveX,编译生成 OCX。


在 Web 中进行测试

  在 Web 中测试也比较简单,通过 clsid 引入 OCX 文件,然后 JS 调用 OCX 文件中的函数,函数返回 MAC 地址给 JS,JS 进行 DOM 操作,代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf8">
<title>获取Mac地址Demo</title>
</head>
<body>
<object id="getmac" classid="clsid:52931A6A-93B4-4750-8FE6-B666E90B1D54"
codebase="'getmac.ocx'" style="display:none"></object>
<h1>MAC:</h1>
<span id="mac"></span>
<input type="text" id="macAddress" name="macAddress" value="" />
</body>
<script>
var mac = getmac.GetMacAddress();
document.getElementById("mac").innerHTML = mac;
document.getElementById("macAddress").value = mac;
document.getElementById("macAddress").style.display = '';
</script>
</html>

  通过 object 标签引入了 OCX 文件,定义了 id 为 getmac,然后通过 getmac 来调用 OCX 中的函数 GetMacAddress() 获取 MAC 地址。


  经过简单的测试还是可以的,然后我整合进入了 JeeSite 系统中(后端是用 Java 的开源项目 JeeSite 写的),测试以后发现 ERP 的页面对 IE 浏览器支持不好。​因为 OCX 只能在 IE 浏览器中使用,结果这个方案就放弃了。​后来,找到一个开源的 Chrome 的插件,也完成 MAC 地址的获取,该 Chrome 插件分为两部分,一部分是 Chrome 的插件,另外一个是 EXE 文件,该 EXE 文件也是与插件进行通信的,由于这个 Chrome 插件不是我写的,我就不往这里放了。其实,Chrome 的插件也只支持 Chrome 浏览器,如果客户使用的是 FireFox 浏览器的话又会有兼容性的问题,因此事后还找到了其他的解决方法,其他的方案就不再依赖插件了,也就和浏览器无关了,也就不存在兼容性的问题了,不过想到那个解决方法时,那个项目其实已经凉了,至于解决的方法有机会再接着介绍吧。