vue項目中極驗驗證的使用代碼示例
使用vue、react的項目獲取數據、上傳數據、注冊、登陸等都是通過接口來完成的,接口很容易被人惡意調用,最容易被人惡意調用的接口就是注冊、登陸類的接口,為了防止接口被惡意調用很多公司開發(fā)了出了很多的人機驗證的工具,今天就來講下極驗驗證在vue項目中的使用。
效果預覽

1、遇到的問題
- 在項目中任何一個頁面或vue組件都有可能需要使用到極驗,并且極驗在初始化時需要傳遞一些參數,要怎么做才能做到每一個組件都能很方便的使用極驗呢?
- 極驗應該在什么時候初始化?是組件一加載就初始化還是用戶點擊按鈕后再初始化?
- 在多語言項目中,用戶手動切換語言后的極驗重置
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(){
...你的業(yè)務邏輯
/*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){
...你的業(yè)務邏輯
}else{
// 重置極驗,使極驗回到可操作狀態(tài)
this.geetestReset();
}
})
.catch(() => {
this.committing = false;
// 重置極驗,使極驗回到可操作狀態(tài)
this.geetestReset();
});
},
// 極驗驗證成功后回調
onGeetestSuccess(){
// 用戶通過極驗行為驗證后做的操作
this.commitData();
},
// 極驗被禁用后回調
onDisableGeetest(){
this.commitData();
}
},
computed: {
...mapGetters(['get_lang'])
}
};
3、極驗初始化時間問題
geetest-mixin.js設計的比較靈活,它可以允許你在任何時機初始化極驗。但在項目中推薦在需要使用到的時候再初始化,1來可以節(jié)省流量;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來進行判斷
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Vuex報錯之[vuex] unknown mutation type: han
這篇文章主要介紹了Vuex報錯之[vuex] unknown mutation type: handlePower問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07

