您好,登錄后才能下訂單哦!
使用vue、react的項目獲取數據、上傳數據、注冊、登陸等都是通過接口來完成的,接口很容易被人惡意調用,最容易被人惡意調用的接口就是注冊、登陸類的接口,為了防止接口被惡意調用很多公司開發了出了很多的人機驗證的工具,今天就來講下極驗驗證
在vue項目中的使用。
效果預覽
1、遇到的問題
2、代碼分享
為了在多個頁面或多個組件中都能方便的使用極驗,我將極驗初始化的相關代碼寫到了mixins
中。這樣做的好處是:方便在組件中獲取極驗相關的數據,以及調用極驗相關api,如做重置、銷毀等操作;缺點是:在同一個頁面中無法在多個地方使用mixin,但這也是有解決方案的。
geetest-mixin.js
/* 極驗mixin */ // 導入極驗官方給的代碼 import gt from "../common/js/geetest/geetest.gt"; import {commonApi} from "../api/commonApi"; import {mapGetters} from "vuex"; // 自定義語言與極驗語言對應表 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) // 極驗默認配置 const geetestOptions = { product: 'popup', // 極驗展現形式 可選值有 float、popup、custom、bind width: '100%', lang: 'zh_CN', autoShow: true, // 當product為bind時,如果次參數為true,則在極驗加載完成后立即顯示極驗彈窗 area: null, // 極驗綁定的元素,僅在 product為 custom、float、popup時需要傳遞 autoRefreshOnLangChange: true, // 語言改變時是否自動刷新 }; export const geetestMixin = { data(){ return { geetest: { geetestSuccessData: null, // 極驗用戶行為操作成功后的數據 geetestObj: null, // 極驗對象 geetestLoading: false, geetestFatched: false, // 判斷是否從服務器獲取了極驗數據 showGeetest: false, // 是否使用極驗 sign: "", // 極驗降級 用的簽名 geetestRestartCountMax: 5, // 極驗重試最大此時 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: { // 初始化極驗 initGeetest(options){ if(!options || ({}).toString.call(options) !== '[object Object]'){ console.error('initGeetest方法的參數options必須是一個對象!'); return; } let newOptions = Object.assign({}, geetestOptions, options); if((newOptions.product === 'popup' || newOptions.product === 'custom' || newOptions.product === 'float') && !newOptions.area){ console.error('product為popup、custom、float時options參數中必須有area屬性,area屬性值可以為css選擇器或dom元素!'); return; } this.geetest.geetestOptions = newOptions; this._geetestRegist_(newOptions); }, // 重置極驗 geetestReset(cb){ if(this.geetest.geetestObj){ this.geetest.geetestSuccessData = {}; this.geetest.geetestObj.reset(); if(typeof cb === 'function'){ cb(); } }else{ console.error('極驗不存在!'); } }, // 顯示極驗彈窗,此方法只有在product為bind時才有效 geetestShow(){ if(this.geetest.geetestObj){ if(this.geetest.geetestOptions.product === 'bind'){ this.geetest.geetestObj.verify(); }else{ console.error('極驗的product值非bind,無法調用show!'); } }else{ console.error('極驗不存在!'); } }, // 初始化極驗,內部使用 _initGeetestInternal_(data, options){ let that = this; let geetest = this.geetest; window.initGeetest({ // 以下 4 個配置參數為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶后臺檢測極驗服務器是否宕機 new_captcha: true, // 用于宕機時表示是新驗證碼的宕機 product: options.product, // 產品形式,包括:float,popup,bind width: options.width, lang: geetestLangMap[options.lang] }, function (captchaObj) { if(geetest.geetestObj){ try { // 如果之前已經初始化過了,則線將之前生成的dom移除掉 geetest.geetestObj.destroy(); }catch (e) { console.error('極驗銷毀失敗', e); } } geetest.geetestObj = captchaObj; if((options.product === 'popup' || options.product === 'custom' || options.product === 'float')){ captchaObj.appendTo(options.area); } // 為bind模式時極驗加載完成后自動彈出極驗彈窗 if(options.autoShow && options.product === 'bind'){ captchaObj.onReady(() => { captchaObj.verify(); }); } geetest.geetestSuccessData = {}; // 當用戶操作后并且通過驗證后的回調 captchaObj.onSuccess(function () { let successData = captchaObj.getValidate(); geetest.geetestSuccessData = successData; console.log('用戶行為驗證通過數據', successData); /* 這種方式不可采用,原因,作用域會被緩存 if (typeof options.callback === 'function') { options.callback(successData); } 用戶行為驗證通過后調用回調函數 */ if(typeof that.onGeetestSuccess === 'function'){ that.onGeetestSuccess(successData); } }); captchaObj.onError(function (e) { console.error("極驗出錯了", e); }); console.log('極驗實例', captchaObj); }); }, // 調用接口,獲取極驗數據 _geetestRegist_(options){ if(this.geetest.geetestLoading){ return; } this.geetest.geetestLoading = true; commonApi.getGtCaptcha() .then(res => { let data = res.data; // TIP 后臺需要控制是否開啟極驗,因此需要判斷 enable===true && success===1 才顯示極限 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; /*// 如果極驗禁用,則調用onDisableGeetest回調 if(typeof options.onDisableGeetest === 'function'){ options.onDisableGeetest(); }*/ // 如果極驗禁用,則調用onDisableGeetest回調 if(typeof this.onDisableGeetest === 'function'){ this.onDisableGeetest(); } return } this.geetest.geetestLoading = false; this._initGeetestInternal_(data, options); }) .catch((err) => { console.error('極驗初始化失敗', err); if(this.geetest.count > this.geetest.geetestRestartCountMax){ this.geetest.geetestLoading = false; return; } console.log('正在重試初始化極驗!當前次數:' + this.geetest.count); this.geetest.count++; this._geetestRegist_(options); }); } }, beforeDestroy(){ if(this.geetest.geetestObj){ this.geetest.geetestObj.destroy(); } } };
geetest.gt.js
段代碼可以不用關注,極驗官網有
/* initGeetest 1.0.0 * 用于加載id對應的驗證碼庫,并支持宕機模式 * 暴露 initGeetest 進行驗證碼的初始化 * 一般不需要用戶進行修改 */ 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: '網絡錯誤' }; 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 }
頁面中使用
// 導入極驗驗證 import {geetestMixin} from "./geetest-mixin"; import {mapGetters} from "vuex"; import {commonApi} from "../api/commonApi"; export default { name: 'Regist', mixins: [geetestMixin], data(){ return { form: { ...表單數據 }, committing: false, errMsg: ' ',. } }, methods: { // 提交注冊數據 submitRegistData(){ ...你的業務邏輯 /*if(this.geetest.showGeetest){ // 如果沒有geetest_challenge,則說明用戶沒有進行行為驗證 if(!this.geetest.geetestSuccessData.geetest_challenge){ this.errMsg = this.$t('formError.geetest'); // 點擊按鈕進行驗證 return; } }*/ this.errMsg = ''; if(!this.geetest.geetestObj){ /* 如果this.geetest.geetestFatched==true,則說明已經加載過極驗了 如果this.geetest.showGeetest==false,則說明后臺關閉極驗了 */ 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(); } } }, // 提交數據 commitData(){ if(this.committing){ return; } this.committing = true; let data = { ...this.form }; let geetestData = {}; // 獲取極驗數據 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){ ...你的業務邏輯 }else{ // 重置極驗,使極驗回到可操作狀態 this.geetestReset(); } }) .catch(() => { this.committing = false; // 重置極驗,使極驗回到可操作狀態 this.geetestReset(); }); }, // 極驗驗證成功后回調 onGeetestSuccess(){ // 用戶通過極驗行為驗證后做的操作 this.commitData(); }, // 極驗被禁用后回調 onDisableGeetest(){ this.commitData(); } }, computed: { ...mapGetters(['get_lang']) } };
3、極驗初始化時間問題
geetest-mixin.js
設計的比較靈活,它可以允許你在任何時機初始化極驗。但在項目中推薦在需要使用到的時候再初始化
,1來可以節省流量;2來可以提升頁面性能;3是最重要的一個點,在單頁面應用程序中都是通過接口來訪問數據的,而接口都有過期時間,如果組件初始化完成就立即初始化極驗,而用戶在接口過期后再去操作則會出現一些奇葩的bug
4、多語言項目中用戶手動切換語言的問題
由于單頁面應用程序切換語言后頁面不會刷新,所以就會出現頁面語言已經切換了,但極驗還是使用的原來的語言。我的解決方案就是在用戶切換語言后手動的刷新一下極驗
{ watch: { get_lang(lang){ let options = this.geetest.geetestOptions; // 如果開啟了語言切換自手動刷新極驗,并且極驗已經初始化了則刷新。如果極驗都還沒有初始化則可以不用去刷新 if(options.autoRefreshOnLangChange && this.geetest.geetestObj){ this.initGeetest({ ...options, lang: lang.code }); } } } }
5、關于點擊按鈕時按鈕loading效果的控制
如《效果預覽》圖中的獲取驗證碼
loading效果控制,可以通過geetest.geetestLoading
來進行判斷
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。