The use of polar verification in vue project

The projects using vue and react to obtain data, upload data, register and log in are all completed through interfaces, which are easy to be called maliciously by people. The interface that is most easy to be called maliciously by people is the interface of registration and log in. In order to prevent the interface from being called maliciously, many companies have developed many human-computer verification tools. Today, the next extreme verification is in the vue item Use in the project.

Effect preview

1. Problems encountered

  1. Any page or vue component in the project may need to use the polar experience, and the polar experience needs to pass some parameters during initialization. What can we do to make every component use the polar experience conveniently?
  2. When should polar verification be initialized? Is it initialized as soon as the component is loaded or after the user clicks the button?
  3. In a multilingual project, the polar reset after the user manually switches the language

2. Code sharing

In order to use polar validation conveniently in multiple pages or components, I wrote the code related to polar initialization into mixins. The advantages of this method are: it is convenient to obtain the data related to the polar experience in the component, and to call the polar experience related api, such as resetting, destroying and other operations; the disadvantage is that mixin cannot be used in multiple places in the same page, but there is also a solution.

geetest-mixin.js

/*
  Extreme mixin
 */
// Import the code given by polar inspection official
import gt from "../common/js/geetest/geetest.gt";
import {commonApi} from "../api/commonApi";
import {mapGetters} from "vuex";
// Correspondence table of user defined language and polar language
const geetestLangMap = {
  "zh_CN": "zh-cn",
  "zh_TW": "zh-tw",
  "en_US": "en",
  "ja_JP": "ja",
  "ko_KR": "ko",
  "ru_RU": "ru"
};
console.log('gt',gt)
// Polar default configuration
const geetestOptions = {
  product: 'popup', // The optional values of polar presentation are float, pop, custom and bind
  width: '100%',
  lang: 'zh_CN',
  autoShow: true, // When product is bind, if the secondary parameter is true, polar pop-up window will be displayed immediately after polar loading
  area: null, // The elements of a polar binding need to be passed only when the product is custom, float, and pop
  autoRefreshOnLangChange: true, // Whether to refresh automatically when the language changes
};
export const geetestMixin = {
  data(){
    return {
      geetest: {
        geetestSuccessData: null, // Data after successful user behavior operation
        geetestObj: null, // Extreme object
        geetestLoading: false,
        geetestFatched: false, // Determine whether the polar data is obtained from the server
        showGeetest: false, // Use polar inspection or not
        sign: "", // Signature for polar degradation
        geetestRestartCountMax: 5, // Maximum retry time
        count: 1,
        geetestOptions: {}
      }
    }
  },
  computed: {
    ...mapGetters(['get_lang'])
  },
  watch: {
    get_lang(lang){
      let options = this.geetest.geetestOptions;
      if(options.autoRefreshOnLangChange && this.geetest.geetestObj){
        this.initGeetest({
          ...options,
          lang: lang.code
        });
      }
    }
  },
  methods: {
    // Initialization polar
    initGeetest(options){
      if(!options || ({}).toString.call(options) !== '[object Object]'){
        console.error('initGeetest Method parameters options Must be an object!');
        return;
      }
      let newOptions = Object.assign({}, geetestOptions, options);
      if((newOptions.product === 'popup' || newOptions.product === 'custom' || newOptions.product === 'float') && !newOptions.area){
        console.error('product by popup,custom,float Time options Parameter must have area Attribute, area Property value can be css Selector or dom Element!');
        return;
      }
      this.geetest.geetestOptions = newOptions;
      this._geetestRegist_(newOptions);
    },
    // Reset test
    geetestReset(cb){
      if(this.geetest.geetestObj){
        this.geetest.geetestSuccessData = {};
        this.geetest.geetestObj.reset();
        if(typeof cb === 'function'){
          cb();
        }
      }else{
        console.error('Polar test does not exist!');
      }
    },
    // This method is only valid when the product is bind
    geetestShow(){
      if(this.geetest.geetestObj){
        if(this.geetest.geetestOptions.product === 'bind'){
          this.geetest.geetestObj.verify();
        }else{
          console.error('Extreme product Value not bind´╝îUnable to call show!');
        }
      }else{
        console.error('Polar test does not exist!');
      }
    },
    // Initialization polar, internal use
    _initGeetestInternal_(data, options){
      let that = this;
      let geetest = this.geetest;

      window.initGeetest({
        // The following 4 configuration parameters are required and cannot be missing
        gt: data.gt,
        challenge: data.challenge,
        offline: !data.success, // Indicates whether the user background checks whether the extreme server is down
        new_captcha: true, // For downtime, it means that the new verification code is down
        product: options.product, // Product form, including: float, pop, bind
        width: options.width,
        lang: geetestLangMap[options.lang]
      }, function (captchaObj) {
                if(geetest.geetestObj){
                  try {
                    // If it has been initialized before, the line will remove the previously generated dom
                    geetest.geetestObj.destroy();
                  }catch (e) {
                    console.error('Extreme destruction failed', e);
                  }
                }
        geetest.geetestObj = captchaObj;
        if((options.product === 'popup' || options.product === 'custom' || options.product === 'float')){
          captchaObj.appendTo(options.area);
        }
        // When it is in bind mode, polar verification pop-up window will pop up automatically after polar verification loading is completed
        if(options.autoShow && options.product === 'bind'){
          captchaObj.onReady(() => {
            captchaObj.verify();
          });
        }
        geetest.geetestSuccessData = {};
        // Callback after user operation and verification
        captchaObj.onSuccess(function () {
          let successData = captchaObj.getValidate();
          geetest.geetestSuccessData = successData;
          console.log('User behavior validation passed data', successData);
          /*
            This method cannot be adopted, because the scope will be cached
            if (typeof options.callback === 'function') {
              options.callback(successData);
            }
            Callback function is called after user behavior validation.
          */
          if(typeof that.onGeetestSuccess === 'function'){
            that.onGeetestSuccess(successData);
          }
        });
        captchaObj.onError(function (e) {
          console.error("The polar test is wrong", e);
        });
        console.log('Proven example', captchaObj);
      });
    },
    // Call interface to obtain polar data
    _geetestRegist_(options){
      if(this.geetest.geetestLoading){
        return;
      }
      this.geetest.geetestLoading = true;
      commonApi.getGtCaptcha()
        .then(res => {
          let data = res.data;
          // The TIP background needs to control whether to turn on the polar check, so it needs to judge enable = true & & success = 1 to display the limit
          this.geetest.sign = data.sign;
          this.geetest.geetestFatched = true;
          if(typeof data.enable == "undefined" || (data.enable === true && data.success === 1)) {
            this.geetest.showGeetest = true;
          }else{
            this.geetest.showGeetest = false;
            this.geetest.geetestLoading = false;
            /*// If polar is disabled, the onDisableGeetest callback is called
            if(typeof options.onDisableGeetest === 'function'){
              options.onDisableGeetest();
            }*/
            // If polar is disabled, the onDisableGeetest callback is called
            if(typeof this.onDisableGeetest === 'function'){
              this.onDisableGeetest();
            }
            return
          }
          this.geetest.geetestLoading = false;
          this._initGeetestInternal_(data, options);
        })
        .catch((err) => {
          console.error('Polar initialization failed', err);
          if(this.geetest.count > this.geetest.geetestRestartCountMax){
            this.geetest.geetestLoading = false;
            return;
          }
          console.log('Initializing polar verification again! Current times:' + this.geetest.count);
          this.geetest.count++;
          this._geetestRegist_(options);
        });
    }
  },
  beforeDestroy(){
    if(this.geetest.geetestObj){
      this.geetest.geetestObj.destroy();
    }
  }
};

geetest.gt.js

Section code can be ignored, which is available on the official website

/* initGeetest 1.0.0
 * It is used to load the verification code library corresponding to id, and supports downtime mode
 * Expose initGeetest to initialize the verification code
 * Generally, users are not required to modify
 */
var gtInit = (function (global, factory) {
  "use strict";
  if (typeof module === "object" && typeof module.exports === "object") {
    // CommonJS
    module.exports = global.document ?
      factory(global, true) :
      function (w) {
        if (!w.document) {
          throw new Error("Geetest requires a window with a document");
        }
        return factory(w);
      };
  } else {
    factory(global);
  }
})(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
  "use strict";
  if (typeof window === 'undefined') {
    throw new Error('Geetest requires browser environment');
  }
  var document = window.document;
  var Math = window.Math;
  var head = document.getElementsByTagName("head")[0];

  function _Object(obj) {
    this._obj = obj;
  }

  _Object.prototype = {
    _each: function (process) {
      var _obj = this._obj;
      for (var k in _obj) {
        if (_obj.hasOwnProperty(k)) {
          process(k, _obj[k]);
        }
      }
      return this;
    }
  };
  function Config(config) {
    var self = this;
    new _Object(config)._each(function (key, value) {
      self[key] = value;
    });
  }

  Config.prototype = {
    api_server: 'api.geetest.com',
    protocol: 'http://',
    type_path: '/gettype.php',
    fallback_config: {
      slide: {
        static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
        type: 'slide',
        slide: '/static/js/geetest.0.0.0.js'
      },
      fullpage: {
        static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
        type: 'fullpage',
        fullpage: '/static/js/fullpage.0.0.0.js'
      }
    },
    _get_fallback_config: function () {
      var self = this;
      if (isString(self.type)) {
        return self.fallback_config[self.type];
      } else if (self.new_captcha) {
        return self.fallback_config.fullpage;
      } else {
        return self.fallback_config.slide;
      }
    },
    _extend: function (obj) {
      var self = this;
      new _Object(obj)._each(function (key, value) {
        self[key] = value;
      })
    }
  };
  var isNumber = function (value) {
    return (typeof value === 'number');
  };
  var isString = function (value) {
    return (typeof value === 'string');
  };
  var isBoolean = function (value) {
    return (typeof value === 'boolean');
  };
  var isObject = function (value) {
    return (typeof value === 'object' && value !== null);
  };
  var isFunction = function (value) {
    return (typeof value === 'function');
  };
  var callbacks = {};
  var status = {};
  var random = function () {
    return parseInt(Math.random() * 10000) + (new Date()).valueOf();
  };
  var loadScript = function (url, cb) {
    var script = document.createElement("script");
    script.charset = "UTF-8";
    script.async = true;
    script.onerror = function () {
      cb(true);
    };
    var loaded = false;
    script.onload = script.onreadystatechange = function () {
      if (!loaded &&
        (!script.readyState ||
          "loaded" === script.readyState ||
          "complete" === script.readyState)) {

        loaded = true;
        setTimeout(function () {
          cb(false);
        }, 0);
      }
    };
    script.src = url;
    head.appendChild(script);
  };
  var normalizeDomain = function (domain) {
    return domain.replace(/^https?:\/\/|\/$/g, '');
  };
  var normalizePath = function (path) {
    path = path.replace(/\/+/g, '/');
    if (path.indexOf('/') !== 0) {
      path = '/' + path;
    }
    return path;
  };
  var normalizeQuery = function (query) {
    if (!query) {
      return '';
    }
    var q = '?';
    new _Object(query)._each(function (key, value) {
      if (isString(value) || isNumber(value) || isBoolean(value)) {
        q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
      }
    });
    if (q === '?') {
      q = '';
    }
    return q.replace(/&$/, '');
  };
  var makeURL = function (protocol, domain, path, query) {
    domain = normalizeDomain(domain);

    var url = normalizePath(path) + normalizeQuery(query);
    if (domain) {
      url = protocol + domain + url;
    }

    return url;
  };
  var load = function (protocol, domains, path, query, cb) {
    var tryRequest = function (at) {

      var url = makeURL(protocol, domains[at], path, query);
      loadScript(url, function (err) {
        if (err) {
          if (at >= domains.length - 1) {
            cb(true);
          } else {
            tryRequest(at + 1);
          }
        } else {
          cb(false);
        }
      });
    };
    tryRequest(0);
  };
  var jsonp = function (domains, path, config, callback) {
    if (isObject(config.getLib)) {
      config._extend(config.getLib);
      callback(config);
      return;
    }
    if (config.offline) {
      callback(config._get_fallback_config());
      return;
    }
    var cb = "geetest_" + random();
    window[cb] = function (data) {
      if (data.status === 'success') {
        callback(data.data);
      } else if (!data.status) {
        callback(data);
      } else {
        callback(config._get_fallback_config());
      }
      window[cb] = undefined;
      try {
        delete window[cb];
      } catch (e) {
      }
    };
    load(config.protocol, domains, path, {
      gt: config.gt,
      callback: cb
    }, function (err) {
      if (err) {
        callback(config._get_fallback_config());
      }
    });
  };
  var throwError = function (errorType, config) {
    var errors = {
      networkError: 'network error'
    };
    if (typeof config.onError === 'function') {
      config.onError(errors[errorType]);
    } else {
      throw new Error(errors[errorType]);
    }
  };
  var detect = function () {
    return !!window.Geetest;
  };
  if (detect()) {
    status.slide = "loaded";
  }
  var initGeetest = function (userConfig, callback) {
    var config = new Config(userConfig);
    if (userConfig.https) {
      config.protocol = 'https://';
    } else if (!userConfig.protocol) {
      config.protocol = window.location.protocol + '//';
    }
    jsonp([config.api_server || config.apiserver], config.type_path, config, function (newConfig) {
      var type = newConfig.type;
      var init = function () {
        config._extend(newConfig);
        callback(new window.Geetest(config));
      };
      callbacks[type] = callbacks[type] || [];
      var s = status[type] || 'init';
      if (s === 'init') {
        status[type] = 'loading';
        callbacks[type].push(init);
        load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
          if (err) {
            status[type] = 'fail';
            throwError('networkError', config);
          } else {
            status[type] = 'loaded';
            var cbs = callbacks[type];
            for (var i = 0, len = cbs.length; i < len; i = i + 1) {
              var cb = cbs[i];
              if (isFunction(cb)) {
                cb();
              }
            }
            callbacks[type] = [];
          }
        });
      } else if (s === "loaded") {
        init();
      } else if (s === "fail") {
        throwError('networkError', config);
      } else if (s === "loading") {
        callbacks[type].push(init);
      }
    });
  };
  window.initGeetest = initGeetest;
  return initGeetest;
});

export default {
  gtInit
}

Use in page

// Import polar verification
import {geetestMixin} from "./geetest-mixin";
import {mapGetters} from "vuex";
import {commonApi} from "../api/commonApi";

export default {
  name: 'Regist',
  mixins: [geetestMixin],
  data(){
    return {
      form: {
        ...Form Data
      },
      committing: false,
      errMsg: ' ',.
    }
  },
  methods: {
    // Submit registration data
    submitRegistData(){
      ...Your business logic
      /*if(this.geetest.showGeetest){
        // If there is no geetest'u challenge, then the user does not conduct behavior verification
        if(!this.geetest.geetestSuccessData.geetest_challenge){
          this.errMsg = this.$t('formError.geetest'); // Click the button to verify
          return;
        }
      }*/
      this.errMsg = '';


      if(!this.geetest.geetestObj){
        /*
          If this. Geetest. Geetestfetched = = true, it means that the extreme has been loaded
          If this.geetest.showGeetest==false, the background is turned off
         */
        if(this.geetest.geetestFatched && !this.geetest.showGeetest){
          //this.committing = true;
          this.commitData();
        }else{
          this.initGeetest({
            product: 'bind',
            lang: this.get_lang.code,
          });
        }
      }else{
        if(this.geetest.showGeetest){
          this.geetestShow();
        }else{
          console.log('fasfsafsdfsd')
          //this.committing = true;
          this.commitData();
        }
      }
    },
    // Submit data
    commitData(){
      if(this.committing){
        return;
      }
      this.committing = true;
      let data = {
        ...this.form
      };
      let geetestData = {};
      // Get polar data
      if(this.geetest.showGeetest){
        geetestData = {
          ...this.geetest.geetestSuccessData,
          sign: this.geetest.sign
        }
      }
      if(!this.form.inviteCode){
        delete data.inviteCode;
      }
      commonApi.regist(data, geetestData)
        .then(res => {
          this.committing = false;
          if(res.errcode === 0){
            ...Your business logic
          }else{
          // Reset the polar check to make it operable
            this.geetestReset();
          }
        })
        .catch(() => {
          this.committing = false;
          // Reset the polar check to make it operable
          this.geetestReset();
        });
    },
    // Callback after successful polar verification
    onGeetestSuccess(){
      // Operation of users after verification of polar behavior
      this.commitData();
    },
    // Callback after polar verification is disabled
    onDisableGeetest(){
      this.commitData();
    }
  },
  computed: {
    ...mapGetters(['get_lang'])
  }
};

3. Polar initialization time

geetest-mixin.js is designed to be flexible, allowing you to initialize a polar at any time. But in the project, it is recommended to reinitialize when it needs to be used. 1 can save traffic; 2 can improve page performance; 3 is the most important point. In a single page application, data is accessed through the interface, and the interface has expiration time. If the component initialization is completed, it will be initialized immediately, and the user will operate after the interface expires Some strange bug s

4. The problem of users manually switching languages in multilingual projects

Because the page will not refresh after the single page application switches the language, it will appear that the page language has been switched, but the original language is still used. My solution is to refresh the experience manually after users switch languages

{
  watch: {
    get_lang(lang){
      let options = this.geetest.geetestOptions;
      // If the language switch is enabled to refresh the polar verification manually, and the polar verification has been initialized, refresh. If the polar verification has not been initialized, you can not refresh it
      if(options.autoRefreshOnLangChange && this.geetest.geetestObj){
        this.initGeetest({
          ...options,
          lang: lang.code
        });
      }
    }
  }
}

5. Control of button loading effect when clicking the button

As shown in the "effect preview" figure, the effect control of getting the verification code loading can be judged by geetest.geetest loading

Tags: Front-end Vue React Attribute PHP

Posted on Tue, 03 Dec 2019 11:40:47 -0500 by DrTom