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