最近菜鸟公司要做一个滑动验证,但是看渡一里面说的是:如果是行为验证的话(自动化图灵测试 ------ 一定是服务端验证),后端需要搜集很多东西,包括用户的鼠标轨迹、点击速度等行为,反正就是很复杂,最好上框架,具体见:
www.bilibili.com/video/BV1Re...
但是菜鸟公司不用做这么复杂,就是前端滑动一下,免得别人登录太快的问题,减少攻击次数(其实只是领导感觉有用,其实屁用没有,就是给客户感觉牛皮的样子,手动狗头)!
思路
两个canvas:canvas1上画一张图并用两个圆覆盖在上面(一个正确、一个错误),然后canvas2只画正确的圆覆盖住的那部分图片,然后滑块操作的就是移动整个canvas2,只要停止的X坐标和我设置的正确的X坐标符合一种关系,就验证通过!
实现
菜鸟感觉自己的代码和注释都写得挺好的,这里直接上代码
js
<script setup>
import { ElMessage } from "element-plus";
import { onMounted } from "vue";
const props = defineProps({
// 外面可以传误差值
mistakeValue: {
type: Number,
default: 3
}
});
const emit = defineEmits(["pass"]);
// 随机图片
let imgurl = "https://picsum.photos/300/200";
// 绑定滑块值
let value1 = ref(0);
let movecanvas = ref(null);
let loading = ref(false);
// 保存数据,方便验证失败重绘
let rightX, r1, r2, r1y, r2y, errorX;
// 生成一个随机数 30 - 270之间(不能超出边界)
const random = (min = 31, max = 269) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
const init = () => {
loading.value = true;
// 保存正确的偏移值
rightX = random();
// 两个圆的半径
r1 = random(20, 30);
r2 = r1 - 5;
// 随机生成两个圆的y坐标(不能超出边界)
r1y = random(31, 169);
r2y = random(31, 169);
// 生成一个错误的偏移值,值在30-rightX之间,或者rightX - 270之间
errorX =
rightX + r1 > 200
? random(31, rightX - r1 - r2) // 在30到rightX之间
: random(rightX + r1 + r2, 269); // 在rightX到270之间
};
init();
// 画:验证图
const dorwVerify = () => {
const canvas = document.querySelector("#verify");
const ctx = canvas.getContext("2d");
const img = new Image();
img.src = imgurl;
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// 在canvas上形成两个浅灰色的圆,并覆盖在图片上
ctx.beginPath();
ctx.arc(rightX, r1y, r1, 0, 2 * Math.PI);
ctx.fillStyle = "#eee";
ctx.fill();
ctx.beginPath();
ctx.arc(errorX, r2y, r2, 0, 2 * Math.PI);
ctx.fillStyle = "#eee";
ctx.fill();
};
};
// 画:滑块
const drowMove = () => {
movecanvas.value = document.querySelector("#move");
const movectx = movecanvas.value.getContext("2d");
const moveimg = new Image();
moveimg.src = imgurl;
moveimg.onload = () => {
movecanvas.value.width = moveimg.width;
movecanvas.value.height = moveimg.height;
// 创建圆形路径
movectx.beginPath();
movectx.arc(rightX, r1y, r1, 0, 2 * Math.PI);
movectx.closePath();
// 裁剪画布
movectx.clip();
// 只绘制裁剪区域内的图片
movectx.drawImage(moveimg, 0, 0, movecanvas.value.width, movecanvas.value.height);
movecanvas.value.style.left = -rightX + r1 + "px";
loading.value = false;
};
};
onMounted(() => {
dorwVerify();
drowMove();
});
// 移动设置left
const getvalFun = () => {
const movecanvas = document.querySelector("#move");
movecanvas.style.left = -rightX + r1 + (value1.value / 100) * 400 + "px";
};
// 验证
const passFun = () => {
// 减100:是减去canvas1旁边留给滑块显示的距离
const stopX = (value1.value / 100) * 400 - 100 + r1;
if (Math.abs(stopX - rightX) < props.mistakeValue) {
ElMessage.success("验证成功");
emit("pass");
} else {
ElMessage.error("请重新验证");
value1.value = 0;
init();
dorwVerify();
drowMove();
}
};
// 刷新
const refreshFun = () => {
value1.value = 0;
init();
dorwVerify();
drowMove();
};
</script>
<template>
<div class="w-[400px]" v-loading="loading">
<div class="relative">
<canvas class="absolute h-[200px] w-[300px]" id="move"></canvas>
<canvas class="ml-[100px] h-[200px] w-[300px]" id="verify"></canvas>
</div>
<div>
<el-slider
:step="0.1"
:show-tooltip="false"
v-model="value1"
@input="getvalFun"
@change="passFun"
/>
</div>
<div>
<el-button plain type="primary" @click="refreshFun">看不清,换一张</el-button>
</div>
</div>
</template>
注意
-
这里推荐一个自动生成图片的网站:picsum.photos/ ,在链接后面指定宽度和高度就可以获取对应大小的随机图片,还能指定模糊、灰度等,真的非常的实用!
-
这里菜鸟生成errorX的地方其实可以优化一下,这里感觉还是差点意思!
-
这里画滑块的部分,菜鸟以为会有白色背景,但是其实没有!
实现结果
更多canvas常见操作见:渡一学习笔记:canvas、css滤镜、特效、svg
其他思路
其实菜鸟做完之后发现,如果按照菜鸟的思路来,甚至都不用canvas,直接:滑块用背景定位、验证图也是直接显示img,然后上面盖一个圆就行!