if-captcha
Advanced tools
+1
-1
| { | ||
| "name": "if-captcha", | ||
| "version": "2.0.12", | ||
| "version": "2.0.13", | ||
| "description": "安全验证滑块服务插件(Vue2/Vue3)", | ||
@@ -5,0 +5,0 @@ "main": "./src/components/index.js", |
+48
-2
@@ -70,3 +70,2 @@ ## 如何使用 | ||
| ## Todo | ||
@@ -92,2 +91,49 @@ ### captchaGetUrl 支持 Promise对象 | ||
| } | ||
| } | ||
| } | ||
| ### 示例 | ||
| ``` | ||
| let res = await $ifCaptcha.verify({ | ||
| captchaType: 'blockPuzzle', | ||
| /** | ||
| 劫持请求 | ||
| payload {captchaType: 'blockPuzzle'} 模块抛出的参数 | ||
| format(success:boolean, data) 对返回结果进行格式化 | ||
| */ | ||
| captchaGetUrl: async (payload, format) => { | ||
| // 自定义逻辑 | ||
| let res = await request('https://uc-uat.ifyou.net/uc/api/captcha/get', payload) | ||
| let success = res.repCode == '0000' | ||
| // 对于clickWord类型 需要如下参数 originalImageBase64, token, secretKey, wordList | ||
| const { jigsawImageBase64, originalImageBase64, secretKey, token } = res.repData | ||
| let data = { | ||
| jigsawImageBase64, | ||
| originalImageBase64, | ||
| secretKey, | ||
| token | ||
| } | ||
| // 自定义逻辑结束 !!!注意data结构, 这是模块可以识别的结构 | ||
| return format(success, data) | ||
| }, | ||
| captchaCheckUrl: async (payload, format) => { | ||
| /** 自定义逻辑 */ | ||
| let params = { ...payload, foo: 'Hi, World', noise: { env: 'demo', ver: '1.0' } } | ||
| let res = await request('https://uc-uat.ifyou.net/uc/api/captcha/check', params) | ||
| let success = res.repCode == '0000' | ||
| /** 自定义逻辑结束 */ | ||
| return format(success) | ||
| } | ||
| }); | ||
| if (res) { | ||
| console.log(JSON.stringify(res)) | ||
| } else { | ||
| result.value = '验证失败' | ||
| } | ||
| ``` |
@@ -15,4 +15,4 @@ <template> | ||
| :explain="options.explain" :imgSize="options.imgSize" :blockSize="options.blockSize" | ||
| :barSize="options.barSize" :captchaGetUrl="options.captchaGetUrl" :captchaCheckUrl="options.captchaCheckUrl" :params="options.params" | ||
| ref="instance"></component> | ||
| :barSize="options.barSize" :captchaGetUrl="options.captchaGetUrl" :captchaCheckUrl="options.captchaCheckUrl" | ||
| :params="options.params" ref="instance"></component> | ||
| </div> | ||
@@ -74,7 +74,7 @@ </div> | ||
| captchaGetUrl: { | ||
| type: String, | ||
| type: [String, Function], | ||
| default: '/captcha/get' | ||
| }, | ||
| captchaCheckUrl: { | ||
| type: String, | ||
| type: [String, Function], | ||
| default: '/captcha/check' | ||
@@ -99,3 +99,3 @@ } | ||
| mounted() { | ||
| const { captchaType, mode, vSpace, explain, imgSize, blockSize, barSize, isShow, captchaGetUrl, captchaCheckUrl, params={}} = this; | ||
| const { captchaType, mode, vSpace, explain, imgSize, blockSize, barSize, isShow, captchaGetUrl, captchaCheckUrl, params = {} } = this; | ||
| Object.assign(this.options, { captchaType, mode, vSpace, explain, imgSize, blockSize, barSize, isShow, captchaGetUrl, captchaCheckUrl, params }); | ||
@@ -102,0 +102,0 @@ }, |
| <template> | ||
| <div style="position: relative"> | ||
| <div class="verify-img-out"> | ||
| <div class="verify-img-panel" :style="{ | ||
| width: setSize.imgWidth, | ||
| height: setSize.imgHeight, | ||
| 'background-size': setSize.imgWidth + ' ' + setSize.imgHeight, | ||
| 'margin-bottom': vSpace + 'px', | ||
| }"> | ||
| <div class="verify-refresh" style="z-index: 3" @click="refresh" v-show="showRefresh"> | ||
| <i class="captcha-icon-refresh"></i> | ||
| </div> | ||
| <img :src="'data:image/png;base64,' + pointBackImgBase" ref="canvas" alt="" | ||
| style="width: 100%; height: 100%; display: block" @click="bindingClick ? canvasClick($event) : undefined" /> | ||
| <div v-if="!loading && !error"> | ||
| <div class="verify-img-out"> | ||
| <div class="verify-img-panel" :style="{ | ||
| width: setSize.imgWidth, | ||
| height: setSize.imgHeight, | ||
| 'background-size': setSize.imgWidth + ' ' + setSize.imgHeight, | ||
| 'margin-bottom': vSpace + 'px', | ||
| }"> | ||
| <div class="verify-refresh" style="z-index: 3" @click="refresh" v-show="showRefresh"> | ||
| <i class="captcha-icon-refresh"></i> | ||
| </div> | ||
| <img :src="'data:image/png;base64,' + pointBackImgBase" ref="canvas" alt="" | ||
| style="width: 100%; height: 100%; display: block" @click="bindingClick ? canvasClick($event) : undefined" /> | ||
| <div v-for="(tempPoint, index) in tempPoints" :key="index" class="point-area" :style="{ | ||
| 'background-color': '#1abd6c', | ||
| color: '#fff', | ||
| 'z-index': 9999, | ||
| width: '20px', | ||
| height: '20px', | ||
| 'text-align': 'center', | ||
| 'line-height': '20px', | ||
| 'border-radius': '50%', | ||
| position: 'absolute', | ||
| top: parseInt(tempPoint.y - 10) + 'px', | ||
| left: parseInt(tempPoint.x - 10) + 'px', | ||
| }"> | ||
| {{ index + 1 }} | ||
| <div v-for="(tempPoint, index) in tempPoints" :key="index" class="point-area" :style="{ | ||
| 'background-color': '#1abd6c', | ||
| color: '#fff', | ||
| 'z-index': 9999, | ||
| width: '20px', | ||
| height: '20px', | ||
| 'text-align': 'center', | ||
| 'line-height': '20px', | ||
| 'border-radius': '50%', | ||
| position: 'absolute', | ||
| top: parseInt(tempPoint.y - 10) + 'px', | ||
| left: parseInt(tempPoint.x - 10) + 'px', | ||
| }"> | ||
| {{ index + 1 }} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <!-- 'height': this.barSize.height, --> | ||
| <div class="verify-bar-area" :style="{ | ||
| width: setSize.imgWidth, | ||
| color: this.barAreaColor, | ||
| 'border-color': this.barAreaBorderColor, | ||
| 'line-height': this.barSize.height, | ||
| }"> | ||
| <span class="verify-msg">{{ text }}</span> | ||
| </div> | ||
| </div> | ||
| <!-- 'height': this.barSize.height, --> | ||
| <div class="verify-bar-area" :style="{ | ||
| width: setSize.imgWidth, | ||
| color: this.barAreaColor, | ||
| 'border-color': this.barAreaBorderColor, | ||
| 'line-height': this.barSize.height, | ||
| }"> | ||
| <span class="verify-msg">{{ text }}</span> | ||
| <div v-else-if="!error"> | ||
| <div style="text-align: center"> | ||
| <i class="captcha-icon-refresh captcha-spin" style="opacity:.85"></i> | ||
| <div style="color:#999; margin-top: 10px; font-size: 14px;">加载中...</div> | ||
| </div> | ||
| </div> | ||
| <div v-else> | ||
| <div style="text-align: center"> | ||
| <div style="font-size:14px; color:#f30; margin: 20px auto;">数据获取错误:无法获取安全验证信息</div> | ||
| <a href="javascript:void(0)" @click="refresh">点击重新获取</a> | ||
| </div> | ||
| </div> | ||
| </div> | ||
@@ -93,7 +108,7 @@ </template> | ||
| captchaGetUrl: { | ||
| type: String, | ||
| type: [String, Function], | ||
| default: '/captcha/get' | ||
| }, | ||
| captchaCheckUrl: { | ||
| type: String, | ||
| type: [String, Function], | ||
| default: '/captcha/check' | ||
@@ -129,2 +144,4 @@ }, | ||
| canvas: null, | ||
| loading: true, | ||
| error: null, | ||
| }; | ||
@@ -140,3 +157,11 @@ }, | ||
| methods: { | ||
| init() { | ||
| responseFormat(success, data, error = '请求失败') { | ||
| return { | ||
| repCode: !!success ? '0000' : '6111', | ||
| repData: data, | ||
| repMsg: error | ||
| } | ||
| }, | ||
| async init() { | ||
| //加载页面 | ||
@@ -146,3 +171,3 @@ this.fontPos.splice(0, this.fontPos.length); | ||
| this.num = 1; | ||
| this.getPictrue(); | ||
| await this.getPictrue(); | ||
| this.$nextTick(() => { | ||
@@ -172,3 +197,3 @@ let { imgHeight, imgWidth, barHeight, barWidth } = resetSize(this); | ||
| //等创建坐标执行完 | ||
| setTimeout(() => { | ||
| setTimeout(async () => { | ||
| // var flag = this.comparePos(this.fontPos, this.checkPosArr); | ||
@@ -188,7 +213,22 @@ //发送后端请求 | ||
| token: this.backToken, | ||
| params: this.params | ||
| ...this.params | ||
| }; | ||
| request(this.captchaCheckUrl, data) | ||
| .then((res) => { | ||
| if (res.repCode == "0000") { | ||
| let res; | ||
| try { | ||
| if(typeof this.captchaCheckUrl === 'string') { | ||
| res = await request(this.captchaCheckUrl, data) | ||
| } else if(typeof this.captchaCheckUrl === 'function') { | ||
| res = await this.captchaCheckUrl(data, this.responseFormat) | ||
| if(!res.repCode) { | ||
| let errorMsg = 'captchaCheckUrl必须返回{repCode}' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| } else { | ||
| let errorMsg = 'captchaCheckUrl不是字符串或者函数' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| if (res.repCode == "0000") { | ||
| this.barAreaColor = "#4cae4c"; | ||
@@ -215,6 +255,8 @@ this.barAreaBorderColor = "#5cb85c"; | ||
| } | ||
| }) | ||
| .catch((e) => { | ||
| this.$parent.$emit("error", e); | ||
| }); | ||
| } catch(e) { | ||
| this.$parent.$emit("error", e); | ||
| } finally { | ||
| // | ||
| } | ||
| }, 400); | ||
@@ -237,3 +279,3 @@ } | ||
| refresh() { | ||
| async refresh() { | ||
| this.tempPoints.splice(0, this.tempPoints.length); | ||
@@ -246,25 +288,44 @@ this.barAreaColor = "#000"; | ||
| this.num = 1; | ||
| this.getPictrue(); | ||
| await this.getPictrue(); | ||
| this.text = "验证失败"; | ||
| this.showRefresh = true; | ||
| }, | ||
| getPictrue() { | ||
| async getPictrue() { | ||
| const { captchaType } = this; | ||
| request(this.captchaGetUrl, { captchaType }) | ||
| .then((res) => { | ||
| const { repCode, repData, repMsg } = res; | ||
| if (repCode == "0000") { | ||
| const { originalImageBase64, token, secretKey, wordList } = repData; | ||
| this.pointBackImgBase = originalImageBase64; | ||
| this.backToken = token; | ||
| this.secretKey = secretKey; | ||
| this.poinTextList = wordList; | ||
| this.text = "请依次点击【" + wordList.join(",") + "】"; | ||
| } else { | ||
| this.text = repMsg; | ||
| this.loading = true; | ||
| this.error = null | ||
| let res; | ||
| try { | ||
| if (typeof this.captchaGetUrl === 'string') { | ||
| res = await request(this.captchaGetUrl, { captchaType }) | ||
| } else if (typeof this.captchaGetUrl === 'function') { | ||
| res = await this.captchaGetUrl({ captchaType }, this.responseFormat) | ||
| if (!res.repCode || !res.repData) { | ||
| let errorMsg = 'captchaGetUrl必须返回{repCode, repData, repMsg?}' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| }) | ||
| .catch((e) => { | ||
| this.$parent.$emit("error", e); | ||
| }); | ||
| } else { | ||
| let errorMsg = 'captchaGetUrl不是字符串或者函数' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| const { repCode, repData, repMsg } = res; | ||
| if (repCode == "0000") { | ||
| const { originalImageBase64, token, secretKey, wordList } = repData; | ||
| this.pointBackImgBase = originalImageBase64; | ||
| this.backToken = token; | ||
| this.secretKey = secretKey; | ||
| this.poinTextList = wordList; | ||
| this.text = "请依次点击【" + wordList.join(",") + "】"; | ||
| } else { | ||
| this.text = repMsg; | ||
| } | ||
| } catch (e) { | ||
| this.$parent.$emit("error", e); | ||
| } finally { | ||
| this.loading = false | ||
| } | ||
| }, | ||
@@ -271,0 +332,0 @@ |
@@ -130,7 +130,7 @@ <template> | ||
| captchaGetUrl: { | ||
| type: String, | ||
| type: [String, Function], | ||
| default: '/captcha/get' | ||
| }, | ||
| captchaCheckUrl: { | ||
| type: String, | ||
| type: [String, Function], | ||
| default: '/captcha/check' | ||
@@ -145,3 +145,3 @@ }, | ||
| return { | ||
| loading: false, | ||
| loading: true, | ||
| error: null, | ||
@@ -197,27 +197,53 @@ secretKey: "", //后端返回的ase加密, | ||
| methods: { | ||
| responseFormat(success, data, error = '请求失败'){ | ||
| return { | ||
| repCode: !!success ? '0000' : '6111', | ||
| repData: data, | ||
| repMsg: error | ||
| } | ||
| }, | ||
| // 请求背景图片和验证图片 | ||
| getPictrue() { | ||
| async getPictrue() { | ||
| const { captchaType } = this; | ||
| this.loading = true; | ||
| this.error = null | ||
| request(this.captchaGetUrl, { captchaType }) | ||
| .then((res) => { | ||
| const { repCode, repData, repMsg } = res; | ||
| if (repCode == "0000") { | ||
| const { originalImageBase64, jigsawImageBase64, token, secretKey } = | ||
| repData; | ||
| this.backImgBase = originalImageBase64; | ||
| this.blockBackImgBase = jigsawImageBase64; | ||
| this.backToken = token; | ||
| this.secretKey = secretKey; | ||
| } else { | ||
| this.tipWords = repMsg; | ||
| try { | ||
| let res; | ||
| if(typeof this.captchaGetUrl === 'string') { | ||
| res = await request(this.captchaGetUrl, { captchaType }) | ||
| } else if(typeof this.captchaGetUrl === 'function') { | ||
| res = await this.captchaGetUrl({captchaType}, this.responseFormat) | ||
| if(!res.repCode || !res.repData) { | ||
| let errorMsg = 'captchaGetUrl必须返回{repCode, repData, repMsg?}' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| }) | ||
| .catch((e) => { | ||
| } else { | ||
| let errorMsg = 'captchaGetUrl不是字符串或者函数' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| const { repCode, repData, repMsg } = res; | ||
| if (repCode == "0000") { | ||
| const { originalImageBase64, jigsawImageBase64, token, secretKey } = | ||
| repData; | ||
| this.backImgBase = originalImageBase64; | ||
| this.blockBackImgBase = jigsawImageBase64; | ||
| this.backToken = token; | ||
| this.secretKey = secretKey; | ||
| } else { | ||
| this.error = '无法获取安全验证信息'; | ||
| this.$parent.$emit("error", e); | ||
| }).finally(() => { | ||
| this.loading = false | ||
| }); | ||
| this.tipWords = repMsg; | ||
| } | ||
| } catch(e) { | ||
| this.error = '无法获取安全验证信息'; | ||
| this.$parent.$emit("error", e); | ||
| } finally { | ||
| this.loading = false | ||
| } | ||
| }, | ||
@@ -241,5 +267,5 @@ | ||
| init() { | ||
| async init() { | ||
| this.text = this.explain; | ||
| this.getPictrue(); | ||
| await this.getPictrue(); | ||
| this.$nextTick(() => { | ||
@@ -317,3 +343,3 @@ let { imgHeight, imgWidth, barHeight, barWidth } = resetSize(this); | ||
| end() { | ||
| async end() { | ||
| this.endMovetime = +new Date(); | ||
@@ -336,8 +362,24 @@ //判断是否重合 | ||
| token: this.backToken, | ||
| params: this.params | ||
| ...this.params | ||
| }; | ||
| request(this.captchaCheckUrl, data) | ||
| .then((res) => { | ||
| if (res.repCode == "0000") { | ||
| let res; | ||
| try { | ||
| if(typeof this.captchaCheckUrl === 'string') { | ||
| res = await request(this.captchaCheckUrl, data) | ||
| } else if(typeof this.captchaCheckUrl === 'function') { | ||
| res = await this.captchaCheckUrl(data, this.responseFormat) | ||
| if(!res.repCode) { | ||
| let errorMsg = 'captchaCheckUrl必须返回{repCode}' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| } else { | ||
| let errorMsg = 'captchaCheckUrl不是字符串或者函数' | ||
| console.error(errorMsg) | ||
| throw new Error(errorMsg) | ||
| } | ||
| if (res.repCode == "0000") { | ||
| this.moveBlockBackgroundColor = "#5cb85c"; | ||
@@ -391,11 +433,11 @@ this.leftBarBorderColor = "#5cb85c"; | ||
| } | ||
| }) | ||
| .catch((e) => { | ||
| this.$parent.$emit("error", e); | ||
| this.refresh(); | ||
| }); | ||
| this.status = false; | ||
| } catch(e) { | ||
| this.$parent.$emit("error", e); | ||
| this.refresh(); | ||
| } finally { | ||
| this.status = false; | ||
| } | ||
| } | ||
| }, | ||
| refresh() { | ||
| async refresh() { | ||
| this.$el.style.pointerEvents = 'auto' | ||
@@ -417,3 +459,3 @@ this.showRefresh = true; | ||
| this.getPictrue(); | ||
| await this.getPictrue(); | ||
| setTimeout(() => { | ||
@@ -420,0 +462,0 @@ this.transitionWidth = ""; |
44915
12.67%137
50.55%