修复验证码验证bug

This commit is contained in:
xiangheng
2024-06-11 20:26:20 +08:00
parent 02c50af424
commit b45b3654c1
10 changed files with 151 additions and 220 deletions

1
admin/.gitignore vendored
View File

@@ -34,3 +34,4 @@ components.d.ts
# .env
.env.development
.env.production
test.html

View File

@@ -8,5 +8,22 @@
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"cSpell.words": ["datetimerange", "nprogress", "pinia", "vform"]
"cSpell.words": [
"brotli",
"datetimerange",
"echarts",
"execa",
"highlightjs",
"logicflow",
"nprogress",
"pinia",
"preinstall",
"rushstack",
"tailwindcss",
"unplugin",
"vform",
"vuedraggable",
"vueuse",
"wangeditor"
]
}

View File

@@ -18,16 +18,15 @@
"@logicflow/core": "^1.2.27",
"@logicflow/extension": "^1.2.27",
"@vue/shared": "^3.4.27",
"@vueuse/core": "^10.9.0",
"@vueuse/core": "^10.10.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.8",
"consola": "^3.2.3",
"axios": "^1.7.2",
"crypto-js": "^4.2.0",
"css-color-function": "^1.3.3",
"dayjs": "^1.11.11",
"echarts": "^5.5.0",
"element-plus": "^2.7.2",
"element-plus": "^2.7.5",
"highlight.js": "^11.9.0",
"lodash-es": "^4.17.21",
"nprogress": "^0.2.0",
@@ -36,38 +35,38 @@
"vform3-builds": "^3.0.10",
"vue": "^3.4.27",
"vue-clipboard3": "^2.0.0",
"vue-echarts": "^6.7.2",
"vue-router": "^4.3.2",
"vue-echarts": "^6.7.3",
"vue-router": "^4.3.3",
"vue3-video-play": "^1.3.2",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.10.2",
"@rushstack/eslint-patch": "^1.10.3",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.11",
"@types/node": "^20.14.2",
"@types/nprogress": "^0.2.3",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.26.0",
"execa": "^8.0.1",
"execa": "^9.2.0",
"fs-extra": "^11.2.0",
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"prettier": "^3.3.1",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.77.0",
"tailwindcss": "^3.4.3",
"sass": "^1.77.4",
"tailwindcss": "^3.4.4",
"typescript": "~5.4.5",
"unplugin-auto-import": "^0.17.5",
"unplugin-auto-import": "^0.17.6",
"unplugin-vue-components": "^0.27.0",
"vite": "^5.2.11",
"vite": "^5.2.13",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-style-import": "^2.0.0",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^2.0.16"
"vue-tsc": "^2.0.21"
}
}

View File

@@ -1,5 +1,5 @@
<template>
<div :class="props.mode == 'pop' ? 'mask' : ''" v-show="showBox">
<div :class="props.mode == 'pop' ? 'mask' : ''" v-if="showBox">
<div
:class="props.mode == 'pop' ? 'verify-box' : ''"
:style="{ 'max-width': parseInt(imgSize.width) + 30 + 'px' }"
@@ -65,8 +65,8 @@ const props = defineProps({
type: Object,
default() {
return {
width: '310px',
height: '155px'
width: 310,
height: 155
}
}
},
@@ -133,6 +133,7 @@ defineExpose({ show, refresh, closeBox })
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
user-select: none;
}
.verify-box-top {
padding: 0 15px;
@@ -258,6 +259,7 @@ defineExpose({ show, refresh, closeBox })
position: absolute;
top: 0px;
left: 0;
background: #fff;
cursor: pointer;
@@ -316,10 +318,12 @@ defineExpose({ show, refresh, closeBox })
border: 1px solid #fff;
}
.verify-bar-area .verify-move-block .verify-sub-block {
.verify-img-panel .verify-sub-block {
position: absolute;
text-align: center;
z-index: 3;
top: 0;
bottom: 0;
/* border: 1px solid #fff; */
}

View File

@@ -4,9 +4,9 @@
<div
class="verify-img-panel"
:style="{
width: setSize.imgWidth,
height: setSize.imgHeight,
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
width: imgSize.width + 'px',
height: imgSize.height + 'px',
'margin-bottom': vSpace + 'px'
}"
>
@@ -48,14 +48,14 @@
</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
width: imgSize.width + 'px',
color: barAreaColor,
'border-color': barAreaBorderColor,
'line-height': '40px'
}"
>
<span class="verify-msg">{{ text }}</span>
@@ -67,10 +67,10 @@
* VerifyPoints
* @description 点选
* */
import { resetSize } from '../utils/util'
// import { resetSize } from '../utils/util'
import { aesEncrypt } from '../utils/ase'
import { reqGet, reqCheck } from '../api/index'
import { onMounted, reactive, ref, nextTick, toRefs, getCurrentInstance } from 'vue'
import { onMounted, reactive, ref, toRefs, getCurrentInstance } from 'vue'
export default {
name: 'VerifyPoints',
emits: ['success', 'error'],
@@ -92,17 +92,8 @@ export default {
type: Object,
default() {
return {
width: '310px',
height: '155px'
}
}
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '40px'
width: 310,
height: 155
}
}
}
@@ -118,12 +109,6 @@ export default {
pointBackImgBase = ref(''), //后端获取到的背景图片
pointTextList = ref([]), //后端返回的点击字体顺序
backToken = ref(''), //后端返回的token值
setSize = reactive({
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0
}),
tempPoints = reactive([]),
text = ref(''),
barAreaColor = ref(undefined),
@@ -137,13 +122,6 @@ export default {
checkPosArr.splice(0, checkPosArr.length)
num.value = 1
getPicture()
nextTick(() => {
const { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
setSize.imgHeight = imgHeight
setSize.imgWidth = imgWidth
setSize.barHeight = barHeight
setSize.barWidth = barWidth
})
}
onMounted(() => {
// 禁止拖拽
@@ -158,7 +136,7 @@ export default {
if (num.value == checkNum.value) {
num.value = createPoint(getMousePos(canvas, e))
//按比例转换坐标值
const arr = pointTransform(checkPosArr, setSize)
const arr = pointTransform(checkPosArr)
checkPosArr.length = 0
checkPosArr.push(...arr)
//等创建坐标执行完
@@ -240,10 +218,10 @@ export default {
})
}
//坐标转换函数
const pointTransform = function (pointArr, imgSize) {
const pointTransform = function (pointArr) {
const newPointArr = pointArr.map((p) => {
const x = Math.round((310 * p.x) / parseInt(imgSize.imgWidth))
const y = Math.round((155 * p.y) / parseInt(imgSize.imgHeight))
const x = null // Math.round((310 * p.x) / parseInt(props.imgSize.width))
const y = null //Math.round((155 * p.y) / parseInt(props.imgSize.height))
return { x, y }
})
return newPointArr
@@ -257,7 +235,7 @@ export default {
pointBackImgBase,
pointTextList,
backToken,
setSize,
tempPoints,
text,
barAreaColor,

View File

@@ -2,18 +2,30 @@
<div style="position: relative">
<div
class="verify-img-out"
:style="{ height: parseInt(setSize.imgHeight) + vSpace + 'px' }"
:style="{ height: parseInt(props.imgSize.height) + vSpace + 'px' }"
>
<div
class="verify-img-panel"
:style="{ width: setSize.imgWidth, height: setSize.imgHeight }"
:style="{ width: props.imgSize.width + 'px', height: props.imgSize.height + 'px' }"
>
<img
v-show="backImgBase"
:src="'data:image/png;base64,' + backImgBase"
alt=""
:src="backImgBase"
style="width: 100%; height: 100%; display: block"
/>
<div
class="verify-sub-block"
:style="{
left: moveBlockLeft
}"
>
<img
v-show="blockBackImgBase"
:src="blockBackImgBase"
style="width: 100%; height: 100%; display: block; -webkit-user-drag: none"
/>
</div>
<div class="verify-refresh" @click="refresh" v-show="showRefresh">
<i class="iconfont icon-refresh"></i>
</div>
@@ -31,17 +43,17 @@
<div
class="verify-bar-area"
:style="{
width: setSize.imgWidth,
height: barSize.height,
'line-height': barSize.height
width: props.imgSize.width + 'px',
height: blockSize.height + 'px',
'line-height': blockSize.height + 'px'
}"
>
<span class="verify-msg" v-text="text"></span>
<div
class="verify-left-bar"
:style="{
width: leftBarWidth !== undefined ? leftBarWidth : barSize.height,
height: barSize.height,
width: leftBarWidth !== undefined ? leftBarWidth : blockSize.height + 'px',
height: blockSize.height + 'px',
'border-color': leftBarBorderColor,
transition: transitionWidth
}"
@@ -52,8 +64,8 @@
@touchstart="start"
@mousedown="start"
:style="{
width: barSize.height,
height: barSize.height,
width: blockSize.width + 'px',
height: blockSize.height + 'px',
'background-color': moveBlockBackgroundColor,
left: moveBlockLeft,
transition: transitionLeft
@@ -63,41 +75,20 @@
:class="['verify-icon iconfont', iconClass]"
:style="{ color: iconColor }"
></i>
<div
class="verify-sub-block"
:style="{
width: Math.floor((parseInt(setSize.imgWidth) * 47) / 310) + 'px',
height: setSize.imgHeight,
top: '-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight
}"
>
<img
v-show="blockBackImgBase"
:src="'data:image/png;base64,' + blockBackImgBase"
alt=""
style="
width: 100%;
height: 100%;
display: block;
-webkit-user-drag: none;
"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
<script setup lang="ts">
/**
* VerifySlide
* @description 滑块
* */
import { aesEncrypt } from '../utils/ase'
import { resetSize } from './../utils/util'
// import { resetSize } from './../utils/util'
import { reqGet, reqCheck } from './../api/index'
import { computed, onMounted, reactive, ref, nextTick, toRefs, getCurrentInstance } from 'vue'
import { computed, onMounted, onUnmounted, ref, nextTick, toRefs, getCurrentInstance } from 'vue'
defineOptions({
name: 'VerifySlide'
})
@@ -106,10 +97,7 @@ const props = defineProps({
captchaType: {
type: String
},
type: {
type: String,
default: '1'
},
//弹出式pop固定fixed
mode: {
type: String,
@@ -127,8 +115,8 @@ const props = defineProps({
type: Object,
default() {
return {
width: '310px',
height: '155px'
width: 310,
height: 155
}
}
},
@@ -136,23 +124,24 @@ const props = defineProps({
type: Object,
default() {
return {
width: '50px',
height: '50px'
}
}
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '40px'
width: 47,
height: 47
}
}
}
})
const blockSize = computed(() => {
return {
width: (props.imgSize.width / 310) * props.blockSize.width,
height: props.blockSize.height
}
})
// const defaultImgSize = {
// width: 310,
// height: 155
// }
const { mode, captchaType, blockSize, explain } = toRefs(props)
const { mode, explain } = toRefs(props)
const { proxy } = getCurrentInstance()
const secretKey = ref(''), //后端返回的ase加密秘钥
passFlag = ref(false), //是否通过的标识
@@ -164,12 +153,6 @@ const secretKey = ref(''), //后端返回的ase加密秘钥
tipWords = ref(''),
text = ref(''),
finishText = ref(''),
setSize = reactive({
imgHeight: '',
imgWidth: '',
barHeight: '',
barWidth: ''
}),
moveBlockLeft = ref(undefined),
leftBarWidth = ref(undefined),
// 移动中样式
@@ -190,46 +173,23 @@ const barArea = computed(() => {
function init() {
text.value = explain.value
getPicture()
nextTick(() => {
const { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
setSize.imgHeight = imgHeight
setSize.imgWidth = imgWidth
setSize.barHeight = barHeight
setSize.barWidth = barWidth
proxy.$parent.$emit('ready', proxy)
})
nextTick(() => {})
window.removeEventListener('touchmove', function (e) {
move(e)
})
window.removeEventListener('mousemove', function (e) {
move(e)
})
window.addEventListener('touchmove', move)
window.addEventListener('mousemove', move)
//鼠标松开
window.removeEventListener('touchend', function () {
end()
})
window.removeEventListener('mouseup', function () {
end()
})
window.addEventListener('touchmove', function (e) {
move(e)
})
window.addEventListener('mousemove', function (e) {
move(e)
})
//鼠标松开
window.addEventListener('touchend', function () {
end()
})
window.addEventListener('mouseup', function () {
end()
})
window.addEventListener('touchend', end)
window.addEventListener('mouseup', end)
}
onUnmounted(() => {
window.removeEventListener('touchmove', move)
window.removeEventListener('mousemove', move)
//鼠标松开
window.removeEventListener('touchend', end)
window.removeEventListener('mouseup', end)
})
onMounted(() => {
// 禁止拖拽
init()
@@ -250,6 +210,8 @@ function start(e) {
}
console.log(barArea)
startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
console.log('startLeft', startLeft.value)
startMoveTime.value = +new Date() //开始滑动的时间
if (isEnd.value == false) {
text.value = ''
@@ -273,18 +235,23 @@ function move(e) {
x = e.touches[0].pageX
}
const bar_area_left = barArea.value.getBoundingClientRect().left
let move_block_left = x - bar_area_left //小方块相对于父元素的left值
// console.log('bar_area_left', x, bar_area_left)
let left = x - bar_area_left - startLeft.value //小方块相对于父元素的left值
// @ts-ignore
const w = parseInt(Number(blockSize.value.width) / 2)
if (move_block_left >= barArea.value.offsetWidth - w - 2) {
move_block_left = barArea.value.offsetWidth - w - 2
if (left >= props.imgSize.width - blockSize.value.width) {
left = props.imgSize.width - blockSize.value.width
}
if (move_block_left <= 0) {
move_block_left = w
if (left < 0) {
left = 0
}
//拖动后小方块的left值
moveBlockLeft.value = move_block_left - startLeft.value + 'px'
leftBarWidth.value = move_block_left - startLeft.value + 'px'
moveBlockLeft.value = left + 'px'
leftBarWidth.value = left + 'px'
// console.log(moveBlockLeft.value)
}
}
@@ -294,12 +261,10 @@ function end() {
//判断是否重合
if (status.value && isEnd.value == false) {
let moveLeftDistance = parseInt((moveBlockLeft.value || '').replace('px', ''))
console.log(setSize.imgWidth)
// @ts-ignore
moveLeftDistance = (moveLeftDistance * 310) / parseInt(setSize.imgWidth)
moveLeftDistance = (moveLeftDistance * 310) / parseInt(props.imgSize.width)
const data = {
captchaType: captchaType.value,
captchaType: props.captchaType,
pointJson: secretKey.value
? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value)
: JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
@@ -374,7 +339,7 @@ const refresh = () => {
// 请求背景图片和验证图片
function getPicture() {
const data = {
captchaType: captchaType.value
captchaType: props.captchaType
}
reqGet(data).then((res) => {
if (!res) {
@@ -382,8 +347,8 @@ function getPicture() {
return
}
if (res.repCode == '0000') {
backImgBase.value = res.repData.originalImageBase64
blockBackImgBase.value = res.repData.jigsawImageBase64
backImgBase.value = 'data:image/png;base64,' + res.repData.originalImageBase64
blockBackImgBase.value = 'data:image/png;base64,' + res.repData.jigsawImageBase64
backToken.value = res.repData.token
secretKey.value = res.repData.secretKey
} else {
@@ -391,35 +356,4 @@ function getPicture() {
}
})
}
// return {
// secretKey, //后端返回的ase加密秘钥
// passFlag, //是否通过的标识
// backImgBase, //验证码背景图片
// blockBackImgBase, //验证滑块的背景图片
// backToken, //后端返回的唯一token值
// startMoveTime, //移动开始的时间
// endMovetime, //移动结束的时间
// tipsBackColor, //提示词的背景颜色
// tipWords,
// text,
// finishText,
// setSize,
// top,
// left,
// moveBlockLeft,
// leftBarWidth,
// // 移动中样式
// moveBlockBackgroundColor,
// leftBarBorderColor,
// iconColor,
// iconClass,
// status, //鼠标状态
// isEnd, //是够验证完成
// showRefresh,
// transitionLeft,
// transitionWidth,
// barArea,
// refresh,
// start
// }
</script>

View File

@@ -89,15 +89,10 @@
<Verify
mode="pop"
captchaType="blockPuzzle"
:imgSize="{ width: '400px', height: '200px' }"
captchaType="clickWord"
:imgSize="{ width: 400, height: 200 }"
ref="verifyRef"
@success="handleSuccess"
@error="
(e) => {
console.log(e)
}
"
></Verify>
</div>
</div>

View File

@@ -10,7 +10,7 @@ import Components from 'unplugin-vue-components/vite'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import viteCompression from 'vite-plugin-compression'
// import { visualizer } from 'rollup-plugin-visualizer'
import { visualizer } from 'rollup-plugin-visualizer'
// https://vitejs.dev/config/
export default ({ mode }) => {
@@ -62,17 +62,17 @@ export default ({ mode }) => {
}),
viteCompression({
algorithm: 'gzip'
})
}),
// viteCompression({
// algorithm: 'brotliCompress'
// })
// visualizer({
// gzipSize: true,
// brotliSize: true,
// emitFile: false,
// filename: 'test.html', //分析图生成的文件名
// open: true //如果存在本地服务端口,将在打包后自动展示
// })
visualizer({
gzipSize: true,
brotliSize: true,
emitFile: false,
filename: 'test.html', //分析图生成的文件名
open: true
})
],
resolve: {
alias: {

View File

@@ -9,6 +9,7 @@
"mapstructure",
"rmvb",
"Warnf",
"webp",
"x_admin"
]
}

View File

@@ -85,11 +85,13 @@ func (c *ClickWordCaptchaService) Check(token string, pointJson string) error {
if err != nil {
return err
}
fontsize := c.factory.config.ClickWord.FontSize
for i, pointVO := range cachePoint {
targetPoint := userPoint[i]
fontsize := c.factory.config.ClickWord.FontSize
if targetPoint.X-fontsize > pointVO.X || targetPoint.X > pointVO.X+fontsize || targetPoint.Y-fontsize > pointVO.Y || targetPoint.Y > pointVO.Y+fontsize {
if targetPoint.X >= pointVO.X-10 && targetPoint.X <= pointVO.X+fontsize+10 && targetPoint.Y >= pointVO.Y-10 && targetPoint.Y <= pointVO.Y+fontsize+10 {
} else {
return errors.New("验证失败")
}
}