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
- 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?
- When should polar verification be initialized? Is it initialized as soon as the component is loaded or after the user clicks the button?
- 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 from "../api/commonApi"; import 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 from "./geetest-mixin"; import from "vuex"; import 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