相关文章推荐
越狱的泡面  ·  apple Xcode ...·  4 周前    · 
不爱学习的小虾米  ·  4年前PHP ...·  1 年前    · 
玩手机的楼梯  ·  RuntimeError: CUDA ...·  1 年前    · 

项目有需求,登录前有匿名举报功能,为了避免恶意举报,需标识出访问者进行控制或封禁。首先想到的是获取mac地址,网上资料很多,大体有这几种方案:

获取mac地址

  1. 通过浏览器获取

    浏览器或是利用ActiveX,目前只有IE支持,谷歌和火狐不支持(谷歌和火狐好像有另外的插件可以支持,但没有成熟应用广泛的插件)

  2. 服务器获取,基本思路是先获取ip,根据ip调用nbtstat(响应有点慢) 或 arp命令,示例代码如下:

    public class GetIpAndMac extends HttpServlet{
    	protected void service(HttpServletRequest request,HttpServletResponse res) throws IOException {		
    		try {
    			String clientIp = getIpAddr( request);
    			System.out.println("客户端ip为:"+clientIp);
    			String clientMac = getMACAddress(clientIp);
    			System.out.println("客户端mac为:"+clientMac);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    	 * 通过HttpServletRequest返回IP地址
    	 * @param request HttpServletRequest
    	 * @return ip String
    	 * @throws Exception
    	public String getIpAddr(HttpServletRequest request) throws Exception {
    	    String ip = request.getHeader("x-forwarded-for");
    	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	        ip = request.getHeader("Proxy-Client-IP");
    	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	        ip = request.getHeader("WL-Proxy-Client-IP");
    	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	        ip = request.getHeader("HTTP_CLIENT_IP");
    	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	        ip = request.getHeader("HTTP_X_FORWARDED_FOR");
    	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	        ip = request.getRemoteAddr();
    	    return ip;
    	 * 通过IP地址获取MAC地址
    	 * @param ip String,127.0.0.1格式
    	 * @return mac String
    	 * @throws Exception
    	public String getMACAddress(String ip) throws Exception {
    	    String line = "";
    	    String macAddress = "";
    	    final String MAC_ADDRESS_PREFIX = "MAC Address = ";
    	    final String MAC_ADDRESS_PREFIX1 = "MAC";
    	    final String LOOPBACK_ADDRESS = "127.0.0.1";
    	    //如果为127.0.0.1,则获取本地MAC地址。
    	    if (LOOPBACK_ADDRESS.equals(ip)) {
    	        InetAddress inetAddress = InetAddress.getLocalHost();
    	        //貌似此方法需要JDK1.6。
    	        byte[] mac = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress();
    	        //下面代码是把mac地址拼装成String
    	        StringBuilder sb = new StringBuilder();
    	        for (int i = 0; i < mac.length; i++) {
    	            if (i != 0) {
    	                sb.append("-");
    	            //mac[i] & 0xFF 是为了把byte转化为正整数
    	            String s = Integer.toHexString(mac[i] & 0xFF);
    	            sb.append(s.length() == 1 ? 0 + s : s);
    	        //把字符串所有小写字母改为大写成为正规的mac地址并返回
    	        macAddress = sb.toString().trim().toUpperCase();
    	        return macAddress;
    	    //获取非本地IP的MAC地址
    	    try {
    	        //Process p = Runtime.getRuntime().exec("nbtstat -A " + ip);
    	        Process p = Runtime.getRuntime().exec("arp -a " + ip);
    	        InputStreamReader isr = new InputStreamReader(p.getInputStream(),"GBK");
    	        BufferedReader br = new BufferedReader(isr);
    	        while ((line = br.readLine()) != null) {
    	            if (line != null) {
    	                /*int index = line.indexOf(MAC_ADDRESS_PREFIX1);
    	                if (index != -1) {
    	                    //macAddress = line.substring(index + MAC_ADDRESS_PREFIX.length()).trim().toUpperCase();
    	                	macAddress = line.substring(line.indexOf("=")+1).trim().toUpperCase();
    	            	int index = line.indexOf(ip);
    	                if (index != -1) {
    	                    //macAddress = line.substring(index + MAC_ADDRESS_PREFIX.length()).trim().toUpperCase();
    	                	macAddress = line.split("\\s+")[1].trim().toUpperCase();
    	        br.close();
    	    } catch (IOException e) {
    	        e.printStackTrace(System.out);
    	    return macAddress;
    

    这个方案起初让我看到了曙光,而且在我本地测试没问题。但现实狠狠打了脸,这个方案有下面两个弊端:

    • 提需求的人提醒了我,他们使用NAT接入互联网,NAT???好尴尬,一直都没注意网络方面的知识,查了一下才知道,简单来说就是局域网的电脑对外都只暴露了一个公有ip,那么这个局域网内电脑访问的所有请求得到的ip都是一个,ip都不准确了,mac地址即使获取到有什么意义

    • nbtstat命令只支持windows arp命令也只能在局域网中使用

      所以这个方案,如果你的系统可以保证只在内网使用,那可以尝试,如果是互联网使用,根本不能使用

浏览器唯一标识

​ 由于mac地址那条路走不通了,所以我想如果浏览器带唯一标识就好了,有了这个思路后就想着随便查查,结果查到了浏览器指纹(也有叫用户指纹),这也是业内比较热门的研究领域,真是意外之喜,如果单纯只是为了标识访问者,进行控制的话,这个就可以了。

​ 浏览器指纹是指仅通过浏览器的各种信息,如系统字体、屏幕分辨率、浏览器插件,无需 cookie 等技术,就能近乎绝对定位一个用户,就算使用浏览器的隐私窗口模式,也无法匿名。这是一个被动的识别方式。也就是说,理论上你访问了某一个网站,那么这个网站就能识别到你,虽然不知道你是谁,但你有一个唯一的指纹,将来无论是广告投放、精准推送,还是其他一些关于隐私的事情,都非常方便。详细了解可参考以下链接,本文只是针对FingerPrint2的使用解释,对浏览器指纹不做深入研究。

  1. https://blog.csdn.net/qq_32447301/article/details/88381534
  2. https://www.zhihu.com/question/51410927
  3. https://www.jianshu.com/p/6c41658f9cb7

目前的方案是结合上面第3个链接的方案,cookie+FingerPrint2js实现

  1. 引入FingerPrint2js

    <script src="https://cdn.jsdelivr.net/npm/fingerprintjs2@2.0.6/dist/fingerprint2.min.js"></script>
    
    function getBrowerUUID(){
    	var murmur;
    	/**userAgent:浏览器升级会改变,进而影响计算结果
    	 * plugins:用户安装插件可能性比较大
    	 * fonts:字体基本都相似,但字体很多也会影响速率**/
    	var options = {excludes: {userAgent: true,plugins:true,fonts:true}}
    	var currentBrowserUUID = getcookie('browserUUID');
    	console.log('currentBrowserUUID:'+currentBrowserUUID);
    	if (window.requestIdleCallback) {
    		requestIdleCallback(function () {
    			Fingerprint2.get(options,function (components) {
    				console.log(components) // an array of components: {key: ..., value: ...}
    				var values = components.map(function (component) { return component.value })
    				murmur = Fingerprint2.x64hash128(values.join(''), 31)
    				if(!$chk(currentBrowserUUID)){
    					//如果当前浏览器cookie中没有browserUUID才设置cookie值,如果有还用原来的,减少浏览器属性更改对标识的影响概率
    					setCookie('browserUUID',murmur);
    	} else {
    		setTimeout(function () {
    			Fingerprint2.get(options,function (components) {
    				console.log(components) // an array of components: {key: ..., value: ...}
    				var values = components.map(function (component) { return component.value })
    				murmur = Fingerprint2.x64hash128(values.join(''), 31)
    				if(!$chk(currentBrowserUUID)){
    					setCookie('browserUUID',murmur);
    		}, 500)
    

    其中为了提高计算准确率,避免用户更新浏览器,安装插件等因素影响计算结果,我把userAgent、plugins、fonts三项排除掉了,代码中有注释说明。计算的值压入cookie中,使用时获取就好。但FingerPrint2js并没有实现跨浏览器,而且也不能溯源,但对于控制恶意提交够用了,官方说明准确率达到99%,这个还没有实践,有待考证。

    附录:指纹组件说明

    名称解释:

    取决于浏览器:当用户在同一设备上使用不同的浏览器时,某些组件不会更改,从而使设备可以进行指纹识别

    稳定:每次刷新页面时,某些组件都会更改(“不稳定”)

    组件取决于浏览器稳定
    userAgent(用户代理)
    language(语言)否(大部分时间)
    colorDepth(颜色深度)没有
    deviceMemory(设备内存)没有
    pixelRatio(像素比)没有
    hardwareConcurrency(设备并发线程数)否(但IE不支持)是(但IE不支持)
    screenResolution(屏幕分辨率)没有
    availableScreenResolution(屏幕有效分辨率)没有
    timezoneOffset(时区偏移)没有
    timezone(时区)没有
    sessionStorage
    localStorage(本地存储)
    indexedDb(索引数据库)
    addBehavior(是否支持Behavior,IE5的属性)
    openDatabase(是否支持调用本地数据库)
    cpuClass(所在系统的CPU等级)没有
    platform(客户端操作系统)否(大部分时间)
    doNotTrack(不跟踪)
    plugins
    canvas(帆布)是的,在实践中是(大多数时候)
    webgl是的,在实践中是(大多数时候)
    webglVendorAndRenderer否(大部分时间)
    adBlock(广告阻止)是(但可能取决于时间)
    hasLiedLanguages(用户是否篡改了语言)没有
    hasLiedResolution(用户是否篡改了屏幕分辨率)没有
    hasLiedOs(.用户是否篡改了操作系统)没有
    hasLiedBrowser(用户是否篡改了浏览器)没有
    touchSupport(触摸屏支持)没有
    customEntropyFunction(自定义方法)
    fonts是的(大多数时候)
    audio
    enumerateDevices?-见#498
    项目有需求,登录前有匿名举报功能,为了避免恶意举报,需标识出访问者进行控制或封禁。首先想到的是获取mac地址,网上资料很多,大体有这几种方案:获取mac地址通过浏览器获取浏览器或是利用ActiveX,目前只有IE支持,谷歌和火狐不支持(谷歌和火狐好像有另外的插件可以支持,但没有成熟应用广泛的插件)服务器获取,基本思路是先获取ip,根据ip调用nbtstat(响应有点慢) 或 arp命令,示例代码如下:public class GetIpAndMac extends HttpServlet{
    基于OpenUDID的ANE,使用它可以在AIR项目中通过ActionScript接口来获得设备UDID。 凡是接触过iOS的开发者都清楚每一台iOS设备都有一个唯一的识别号:UDID,这个40位的字符串是你的设备区别于其他任何一台设备的唯一标识。 这个字符串用处非常大,我们可以把它作为用户的唯一ID,跳过用户登陆这一步,直接有效并且安全地与数据库中的用户记录进行绑定。 虽然UDID本身并不含有任何用户信息,但是由于应用开发者可以将UDID与服务器上用户信息进行绑定,从而带来了诸多隐私泄漏等问题,所以苹果最终还是拒绝开发者访问UDID的官方接口,建议开发者使用CFUUID来代替UDID。CFUUID有很多问题,如果从一台设备将系统备份到另一个设备,两个设备就会拥有相同的CFUUID,如果从临时文件中备份系统,就会出现一个设备中出现不同的CFUUID,但是尽管如此,CFUUID还是所有UDID替代品中最靠谱的一个。 AS类OpenUDID是一个静态类,它只有一个静态属性UDID,使用方法用一行代码: var id:String = OpenUDID.UDID;
    1、首先,客户端调用wx.login,回调数据了包含jscode,用于获取openid(用户唯一标识)和sessionkey(会话密钥)。 2、拿到jscode后,将其发送给服务端,服务端拿它与微信服务端做交互获取openid和sessionkey。具体获取方法如下: (1)需要写一个HttpUrlConnection工具类: public class MyHttpUrlConnection { private final int mTimeout = 10000; // 超时时间 * get访问
    在 Web 应用程序中, 可以使用浏览器的 window.navigator.userAgent 属性来获取浏览器的用户代理字符串。这个字符串包含有关浏览器类型和版本的信息, 但是它不是唯一的, 因为不同的浏览器可能具有相同的用户代理字符串。 要获取更加唯一标识符, 可以使用浏览器提供的 Web 技术, 如: 浏览器或操作系统提供的唯一设备 ID: 在 HTML5 中, 可以使用 naviga...
    打破不良API 欢迎使用Breaking Bad API! 本文档将为您提供开始在此标志性电视连续剧中发出HTTP请求所需的所有信息。 在开始项目之前,请先阅读我们的文档,不要忘了冰格! 该API是免费的,我无意创建身份验证。 但是,为防止有害活动,每天的请求速率限制为10,000个。 如果您碰巧达到了该限制,您将收到429状态码,并在24小时后重新获得访问权限。 每个字符的唯一ID 角色的全名 角色的生日 角色的已知职业清单 角色的图片(以jpg格式) 他们还活着吗(还是海森堡找到了他们??) 他们称为的已知昵称 角色出现的季节清单 获取所有角色 端点从所有字符中检索信息。 /api/characters 一个基于python的图像分类调查服务器,用户可以利用api自己上传图像并指定分类,也可以从服务器的图像中查看图片,并指定分类。 由于引入了其他仓库,因此需要使用--recursive进行克隆 git clone --recursive https://github.com/fumiama/image-classification-questionnaire-server.git 接下来,你需要安装cmake,然后执行以下命令以生成程序所需的C库。 mkdir build cd build cmake .. 图片扩展名只接受.webp,如需其它格式请自行修改代码。 图片的唯一标识使用了该图片dhash值的base16384编码的前五个汉字。 0. 直接访问 格式: http://[server_domain]/index.html
    一、什么是User-Agent User-Agent是Http协议中的一部分,属于头域的组成部分,User Agent也简称UA。用较为普通的一点来说,是一种向访问网站提供你所使用的浏览器类型、操作系统及版本、CPU 类型、浏览器渲染引擎、浏览器语言、浏览器插件等信息的标识。UA字符串在每次浏览器 HTTP 请求时发送到服务器! 浏览器UA 字串的标准格式为: 浏览器标识 (操作系...
    在很多移动应用开发中,我们需要获取手机设备的唯一标识,以便于做一些数据统计、用户识别等操作。在H5开发中,我们同样需要获取设备的唯一标识,这时候我们可以利用HTML5提供的一些API来实现。 一、使用HTML5的Web Storage 其中最常见的方式就是利用Web Storage。我们可以使用localStorage来存储一个设备唯一标识的值,当用户首次访问网站时创建一个唯一标识,在以后的访问中直接读取该值。 例如,我们可以在用户首次打开网站时生成一个UUID(Universally Unique Identifier,通用唯一识别码),并将其存入localStorage,然后用户以后的所有访问中直接读取这个值。 但是要注意的是,如果用户清除了浏览器缓存或者使用了不同的设备访问网站,那么就无法获取到之前生成的唯一标识,所以这种方式并不是完全可靠的。 二、使用HTML5的设备指纹 此外,HTML5还提供了一些硬件信息和软件信息,在浏览器中能够生成唯一标识的信息成为“设备指纹”,这些信息包括操作系统、浏览器、系统语言、屏幕分辨率、显示器尺寸、字体、插件等,利用这些信息可以生成唯一标识。 但是设备指纹不能百分之百的标识用户,因为不同的用户的设备指纹也可能很相似。 三、使用第三方API 还有一种方式是使用第三方提供的API,例如Google的Firebase Cloud Messaging API,该API可以生成设备唯一标识,并且可以跨平台使用。 总而言之,获取设备唯一标识并不是一件可以百分之百准确的事情,我们需要根据实际需求来选择合适的获取方式,以及结合其他信息来进行用户识别。