看到很多人还在用政治讨论【环球日报发起释放孟晚舟的联署,被发现作弊】。作为IT资深人士,我几乎一口咖啡喷在屏幕上,只欢迎IT技术人士共同切磋,我虽然已经研究透了这个投票逻辑,基于高尚的品格,只投了一次哈

整个网页,每隔60秒用ajax call 从后台获取实际的签名数字,自己用这个url试https://vote.huanqiu.com/api/v1/num。

它那个动画数字只是为了吸引眼球用的,一段时间就会用ajax call从后台更新实际的数据,

网站更新数据和submit 签名都是asynchronous,它提到是fingerprint防止多次签名,但是实际上非常容易就可以实现多次签名,但是网站的block IP技术还是有些水平的,总之,从网站的设计上看并不能发现作弊,当然后台的数据,就没人知道了


:


// var fgId = '7okx3d4yk2';
// var is_sign = 'is_sign_' + fgId;
// var language1 = 'language_' + fgId;

var u = navigator.userAgent;
var iswindows = (u.indexOf("Windows",0) != -1)?1:0;  
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; 
if(iswindows || isAndroid){
  // $('#clock .time-container .num').css('line-height', '1.2266666666666666rem');
}
// 中英文
renders(language);
function renders(t){
  document.title = languageText[t].headTile;
  $('.numberOf').text(languageText[t].signNum);
  $('.titSmall').html(languageText[t].titSmall);
  $('.slide1 h5').html(languageText[t].tit1);
  $('.slide1 .conts p').html(languageText[t].content1);
  $('.slide2 h5').html(languageText[t].tit2);
  $('.slide2 .conts p').html(languageText[t].content2);
  $('.conts span.packUp').text(languageText[t].packUp);
  $('.foot p').text(languageText[t].footText);
  $('.language').text(languageText[t].btnText);
  
  $('.switch').css('opacity',1);
  $('body,html').animate({ scrollTop: 0 }, 100);;
  isSlide = false;
  messageOut1();
  // 需要重新获取投票数
  getInfo();
  // 初始每分钟获取数据
  perMinutes();
  // 初始化按钮
  // initSignBtn();
}

var isClick;
// 验证本地存的local是否有效
initFingerprintJS(verifyRequest);
var visitorId = '80017177de1cb67cf66ae6c5f1e00008';
function initFingerprintJS(verifyRequest) {
  var fpPromise = FingerprintJS.load(); 
  fpPromise.then(function (fp) {
    return fp.get();
  }).then(function (result) {
    visitorId = result.visitorId;
    // console.log(visitorId);
    verifyRequest(visitorId);
  });
}
function verifyRequest(fingerprint){
  if(localStorage.getItem(is_sign) === 'true' ){
    $('.zhuan').removeClass('zhuanIng').addClass('zhuanEnd');
    $('.btnSig').addClass('btnSigEnd');
    isClick = true;
    return false;
  }
  $.ajax({
    url: '/api/v1/verify',
    type: 'POST',
    data: {"id":fgId,"fingerprint":fingerprint}
  })
  .done(function(res) {
    // 已经签名
    if(res.code !== 0){
      // $('.sign').removeAttr('onclick').text(checkTip('您已经签名了'));
      localStorage.setItem(is_sign, 'true');
      $('.zhuan').removeClass('zhuanIng').addClass('zhuanEnd');
      $('.btnSig').addClass('btnSigEnd');
      isClick = true;
    }
  })
  .fail(function(e) {
    console.log(e.statusText);
  })
  .complete(function() {
  })
}

// 检查不同语言提示语
function checkTip(info){
  var infos = info
  if(language !== 'zh'){
    switch(info){
      case '您已经签名了':
          infos = 'You have signed';
          break;
      case '已经签名过':
          infos = 'You have signed';
          break;
      case '签名失败,请刷新重试':
          infos = 'Failed. Please refresh and retry';
          break;
      case '您已签名,不可重复签名':
          infos = 'You’ve signed. Non-repeatable signature';
          break;
      case '你的网络签名的人太多,请稍后再试':
          infos = 'Too many requests from your intranet. Please retry later';
          break;
      case '签名失败':
          infos = 'Failed.';
          break;
      case '签名成功':
          infos = "You've signed";
          break;
      case '签名失败,您的网络存在频繁操作或作弊行为':
          infos = 'Failed. Frequent attempts or cheating behavior from your intranet';
          break;
      case '签名失败,您的网络或设备存在作弊行为':
          infos = 'Failed. Cheating behavior detected from your intranet or device';
          break;
    }
  }
  return infos;
}

// 点击签名
$('.btnSig').click(function(event) {
  if(isClick){
    return false;
  }
  // $('.add1').removeClass('success');
  isClick = true;
  $('.zhuan').addClass('zhuanIng');
  var issign = localStorage.getItem(is_sign) ? localStorage.getItem(is_sign) : false ;
  issign = (issign === 'true') ? true : false;
  if(issign) {
    message1(checkTip("您已经签名了"));
    localStorage.setItem(is_sign, true);
    $('.zhuan').removeClass('zhuanIng').addClass('zhuanEnd');
    $('.btnSig').addClass('btnSigEnd');
    // $('.sign').removeAttr('onclick').text(checkTip('您已经签名了'));
    return false;
  }
  postRequest(visitorId);
  
});

// 签名-指纹
function postRequest(fingerprint){
  $.ajax({
    url: '/api/v1/sign',
    type: 'POST',
    data: {"id":fgId,"fingerprint":fingerprint}
  })
  .done(function(res) {
    if(res.code === 0){
      $('.zhuan').fadeOut('200', function() {
        $('.zhuan').removeClass('zhuanIng').addClass('zhuanEnd').fadeIn();
        $('.btnSig').addClass('btnSigEnd');
        $('.add1').addClass('success');
      });
      localStorage.setItem(is_sign, true);
      changeEndNum += 1;
      if(changeNum === undefined){
        return false;
      }
      clock(changeNum, changeEndNum);
    }else{
      message1(checkTip(res.msg));
      $('.zhuan').removeClass('zhuanIng');
      if(res.code == -1){
        localStorage.setItem(is_sign, true);
        $('.zhuan').removeClass('zhuanIng').addClass('zhuanEnd');
        $('.btnSig').addClass('btnSigEnd');
      }else if(res.code == 400101){
        localStorage.setItem(is_sign, true);
        $('.zhuan').removeClass('zhuanIng').addClass('zhuanEnd');
        $('.btnSig').addClass('btnSigEnd');
      }else{
        isClick = false;
      }
    }
  })
  .fail(function(e) {
    console.log(e.statusText);
  })
  .complete(function() {
  })
}


// 语言切换
$('.language').click(function(){
  if($(this).text() === '中文'){
      language = 'zh';
      $('.content').removeClass('content_en');
  }else{
      language = 'en';
      $('.content').addClass('content_en');
  }
  localStorage.setItem(language1, language);
  toggleLanguage(language);
  renders(language);
});


// 数字动效
var currentTime = 25000000;
var endTime = 25000000+100;
var changeNum;
var changeEndNum;
var timerId1;
var flagNum;
// clock(currentTime, endTime);
function clock(current, endTime) {
  updateTime();
  function updateTime() {
    var arrNum, seconds, minutes, hours, month, times = {};
    if(current > endTime){
      return false;
    }
    changeNum = current;
    changeEndNum = endTime;
    arrNum = (current + '').split('').reverse();
    for(var i = 0;i< arrNum.length; i++){
      times['num'+(i+1)] = arrNum[i];
    }
    // console.log(current,endTime,arrNum.length,times)
    // if(flagNum !== arrNum.length){
      // $('.time-container').show();
      // for(var i = arrNum.length;i< 11; i++){
      //   $('.num'+(i+1)).hide();
      // }
    // }

    flagNum = arrNum.length

    if(flagNum === 9){
      $('.time-container').show();
    }

    for (type in times) {
      if (times.hasOwnProperty(type)) {
        setTimes(type, times[type]);
      }
    }
    if(timerId1){
      clearTimeout(timerId1)
    }
    // console.log(changeNum,endTime)
    timerId1 = setTimeout(updateTime, 800);
    current++
  }

  function getPreviousTime(type) {
    return $('#' + type + '-top').text();
  }

  function setTimes(type, timeStr) {
    setTime(getPreviousTime(type + '-ones'),
      timeStr, type + '-ones');
  }

  function setTime(previousTime, newTime, type) {
    if (previousTime === newTime) {
      return;
    }
    setTimeout(function() {
      $('#' + type + '-top').text(newTime);
    }, 150);
    setTimeout(function() {
    $('.bottom-container',
      $('#' + type + '-bottom')).text(newTime);
    }, 365);

    animateTime(previousTime, newTime, type);
  }

  function animateTime(previousTime, newTime, type) {
    var top, bottom;
    top = $('#top-' + type + '-anim');
    bottom = $('#bottom-' + type + '-anim');
    $('.top-half-num', top).text(previousTime);
    $('.dropper', bottom).text(newTime);
    top.show();
    bottom.show();
    $('#top-' + type + '-anim').css('visibility', 'visible');
    $('#bottom-' + type + '-anim').css('visibility', 'visible');
    animateNumSwap(type);
    setTimeout(function() {
      hideNumSwap(type);
    }, 365);
  }

  function animateNumSwap(type) {
    $('#top-' + type + '-anim').toggleClass('up');
    $('#bottom-' + type + '-anim').toggleClass('down');
  }

  function hideNumSwap(type) {
    $('#top-' + type + '-anim').toggleClass('up');
    $('#bottom-' + type + '-anim').toggleClass('down');
    $('#top-' + type + '-anim').css('visibility', 'hidden');
    $('#bottom-' + type + '-anim').css('visibility', 'hidden');
  }
}

// 轮播图
var isSlide;
var mySwiper = new Swiper('.swiper',{
  autoplay : 3200,
  loop : true,
  spaceBetween : 30,
  autoplayDisableOnInteraction : false,
  pagination : '.swiper-pagination',
  onSlideChangeStart: function(swiper){
    isSlide = true;
  },
  onSlideChangeEnd: function(swiper){
    $('.swiper-slide .conts .packUp').click();
    isSlide = false;
  }
});

$('.swiper-slide h5').on("click", "span", function(e) {
  if(isSlide){
    return false;
  }
  $(this).hide();
  $(this).parent().siblings('.conts').show();
  $('.swiper-pagination').hide();
  mySwiper.lockSwipes();
  mySwiper.stopAutoplay();
});
$('.swiper-slide .conts .packUp').on("click", function(e) {
  $('.swiper-slide h5 span').show();
  $('.swiper-slide .conts').hide();
  $('.swiper-pagination').show();
  mySwiper.unlockSwipes();
  mySwiper.startAutoplay();
});

// 点击分享
$('.circle .share').click(function(){
  $('html,body').height('100%');
  $('.closed,.closeInfo').show();
  $('.shareImg img').attr('src', $('.shareImg img').attr('data-src'));
  $('.content').css({
    'position': 'fixed',
    'opacity': '0',
    'z-index': '-100'
  });
  $('.shareImg').show().scroll(0,0);
});
$('i.closed').click(function(){
  $('html,body').height('auto');
  $('.closed,.closeInfo').hide();
  $('.shareImg').hide();
  $('.content').css({
    'position': 'relative',
    'opacity': '1',
    'z-index': '1'
  });
  mySwiper.startAutoplay();
  isSlide = false;
  // 开始轮播
});

// 每分钟取一次数据
var timerId0;
function perMinutes(){
  if(timerId0){
    clearInterval(timerId0);
  }
  timerId0 = setInterval(startMarqueue,60000);
}

function startMarqueue(){
  $.ajax({
    url: '/api/v1/num',
    type: 'get'
  })
  .done(function(res) {
    if(res.code == 0){
      res.data = res.data ? res.data: {total: null};
      res.num = res.data.total == null ? 0 : res.data.total;
      changeEndNum = parseInt(res.num);
      clock(changeNum, changeEndNum);
    }
  })
  .fail(function(e) {
    console.log(e.statusText);
  });
}

// 获取信息
var flagInfo = $('#fable').html();
function getInfo(){
  $.ajax({
    url: '/api/v1/info',
    type: 'get'
  })
  .done(function(res) {
    if(res.code == 0){
      res.data = res.data ? res.data: {total: null};
      res.num = res.data.total == null ? 0 : res.data.total;
      $('#num').text(res.num);

      if(!res.data.cities){
        return false;
      }
      var data = res.data.cities;
      var arr = data;
      function compare(property){
          return function(a,b){
              var value1 = a[property];
              var value2 = b[property];
              return value2 - value1;
          }
      }
      data = arr.sort(compare('time'));
      var html = '';
      var current_stamp = Date.parse(new Date())/1000;
      for(var i=0; i<data.length;i++  ){
          var diff = parseInt(current_stamp-data[i].time);
          if(diff<=0){
            diff = 1;
          }
          var timestr = '';
          if(diff<60) timestr = diff+'秒前';
          if(diff>=60 && diff<3600) timestr = Math.floor(diff/60) +'分钟前';
          if(diff>=3600 && diff<86400) timestr = Math.floor(diff/3600)+'小时前';
          if(diff>=86400 && diff<2592000) timestr = Math.floor(diff/86400)+'天前';
          if(diff>=2592000 && diff<31104000) timestr = Math.floor(diff/2592000)+'月前';
          if(diff>=31104000) timestr = Math.floor(diff/31104000)+'年前';
          html+="<h4><i class='iconcs'></i><p><span class='textspan'>来自"+data[i].city+"的网友参加了签名</span><span class='minutespan' >"+timestr+"</span></p></h4>";
      }
      $('#fable').html(flagInfo);
      $('#box').empty().append(html);
      marque(175,148,'fable','box');
      currentTime = res.num || 25000000;
      if(changeNum){
        // console.log('切换获取getInfo')
        if(currentTime > changeEndNum){
          clock(changeNum, currentTime);
          return false;
        }
        clock(changeNum, changeEndNum);
      }else{
        // console.log('获取getInfo')
        if(currentTime<100){
          clock(currentTime-20, currentTime);
          return false;
        }
        clock(currentTime-100, currentTime);
      }
      // console.log('getInfo',currentTime, changeNum,changeEndNum);
    }
  })
  .fail(function(e) {
    console.log(e.statusText);
  })
}
// 网友滚动
// 滚动
var scrollElem;
var stopscroll;
var stoptime;
var preTop;
var leftElem;
var currentTop;
var marqueesHeight;
var timerId3;
var timerId2;
function marque(width,height,marqueName,marqueCName){
  try{
    marqueesHeight = height;
    stopscroll     = false;
    scrollElem = document.getElementById(marqueName);
    with(scrollElem)
    {
      style.width     = width;
      style.height    = marqueesHeight;
      style.overflow  = 'hidden';
      noWrap          = true;
      align           = 'left';
    }
    preTop     = 0;
    currentTop = 0;
    stoptime   = 0;
    leftElem = document.getElementById(marqueCName);
    scrollElem.appendChild(leftElem.cloneNode(true));

    if(timerId3){
      clearTimeout(timerId3);
    }
    timerId3 = setTimeout("init_srolltext()",1000);
  }catch(e) {}
}
function init_srolltext(){
  scrollElem.scrollTop = 0;
  if(timerId2){
    clearTimeout(timerId2);
  }
  timerId2 = setInterval('scrollUp()', 30);
}
function scrollUp(){
  if(stopscroll) return;
  currentTop += 1;
  if(currentTop == marqueesHeight+1){
    stoptime += 1;
    currentTop -= 1;
    if(stoptime == 1) {
      currentTop = 0;
      stoptime = 0;
    }
  }else{
    preTop = scrollElem.scrollTop;
    scrollElem.scrollTop += 2;
    if(preTop == scrollElem.scrollTop){
      scrollElem.scrollTop = marqueesHeight;
      scrollElem.scrollTop += 2;
    }
  }
}

// 弹框封装
function messageShow1() {
    var w = $('.message1');
    w.addClass('messageShow');
};
function messageHide1() {
    var w = $('.message1');
    w.removeClass('messageShow').removeClass('messageShowNavFix');
};
function messageOut1() {
    var w = $('.message1');
    w.css('display', 'none');
    w.html('');
};
var t1, t2, t3;
function message1(str) {
    var w = $('.message1');
    clearTimeout(t1);
    clearTimeout(t2);
    clearTimeout(t3);
    w.html(str);
    w.css('display', 'block');
    t1 = setTimeout("messageShow1()", 80);
    t2 = setTimeout("messageHide1()", 2500);
    t3 = setTimeout("messageOut1()", 3000);
};

 

-keysi(K.S) 2021-8-22