讨厌什么变成什么 2019-11-04
细心的同学可以发现,现在很多网站当登录多次之后就会出现一个图形验证码,或是当提交表单、或点击获取手机验证码等等场景都会有图形验证码的出现。
那么图形验证码是为了解决什么问题而出现的呢?
图形验证码是验证码的一种。验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式。
既然图形验证码是为了区分机器和人之间的操作,那么我们就可以在图形上绘制一个只有人可以解答的问题。比较常见的是在图片上生成文字验证码,然后用户输入图片上的文字吻合则验证通过。
虽然这种验证方法已经渐渐的被其他更先进的方法所淘汰了(图片上的文字依然可以被程序识别读取),并且前端生成验证码的方式相较于后端安全性不高,但我们的目的只是为了装x,提升程序的安全性只是附带的效果。
首先我们需要在在登录表单上额外添加用于输入验证码的FormItem
,并且给图形验证码提供一个canvas
容器。有时候生成的验证码看不明白,因此需要给验证码添加点击事件用以切换验证码:
<Form ref="loginForm" :model="form" :rules="rules"> <FormItem prop="userName"> <Input v-model="form.userName" placeholder="请输入用户名"> <span slot="prepend"> <Icon :size="16" type="person"></Icon> </span> </Input> </FormItem> <FormItem prop="password"> <Input type="password" v-model="form.password" placeholder="请输入密码"> <span slot="prepend"> <Icon :size="14" type="locked"></Icon> </span> </Input> </FormItem> <FormItem prop="valiCode" v-show="this.count"> <Input v-model="form.valiCode" placeholder="请输入验证码"> <span slot="prepend"> <Icon :size="14" type="ios-analytics"></Icon> </span> </Input> <div class="canvas" @click="getImgYanzheng"> <canvas id="canvas"></canvas> </div> </FormItem> <FormItem> <Button @click="handleSubmit" type="primary" long>登录</Button> </FormItem> </Form>
表单需要额外添加valiCode
用以记录用户输入的验证码。此处我们定义当用户登录失败一次则需要额外输入图形验证码,因此添加count
属性,当登陆失败时count++
,当然这样的处理方式并不是很严谨,并且用户刷新页面count
则会清零。可以在此处可以增加更多限制,如异地登录等,由于本案例完全没有涉及到后端程序,因此只是简单的以count
为判断依据。
data() { return { form: { userName: "",// 用户名 password: "",// 密码 valiCode: ""// 验证码 }, count: 0, // 登录次数 show_num: [],// 图形上的文字 } }
页面上为canvas
容器绑定的方法getImgYanzheng
就是在绘制图形验证码
。在绘制图形验证码时需要为你的验证码定义一个内容集合,此处使用的是:A,B,C,E,F,G,H,J,K,M,N,P,Q,R,S,T,W,X,Y,Z,1,2,3,4,5,6,7,8,9,0,好,医,生
。字母中剔除了容易误识别的几个字母并且可以随意加入文字(因此图形验证码也可在做成随机生成四个文字让用户点击,或者生成成语让用户填空等等各种形式)。并且忽略用户大小写,因此需要用到toLowerCase
方法。
接下来就是canvas
绘图的一些技巧了。
canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成:
var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d");
在JavaScript 中使用 id 来寻找 canvas 元素,然后创建context对象,getContext("2d")
对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。我们可以把canvas 想象成景色而context则是景色呈现的画布。
由于绘制验证码的过程中是从左往右绘制的,因此需要规划好画布的使用范围,另外在验证码绘制时还要加上一些随机的元素使验证码不容易被程序识别。
getImgYanzheng() { var show_num = []; var canvas_width = 150; //document.getElementById("canvas").style.width; var canvas_height = 30; //document.getElementById("canvas").style.height; var canvas = document.getElementById("canvas"); //获取到canvas的对象,景色 var context = canvas.getContext("2d"); //获取到canvas画图的环境,景色呈现的画布 canvas.width = canvas_width; canvas.height = canvas_height; var sCode = "A,B,C,E,F,G,H,J,K,M,N,P,Q,R,S,T,W,X,Y,Z,1,2,3,4,5,6,7,8,9,0,好,医,生"; var aCode = sCode.split(","); var aLength = aCode.length; //获取到数组的长度 for (var i = 0; i <= 3; i++) { var j = Math.floor(Math.random() * aLength); //获取到随机的索引值 var deg = (Math.random() * 30 * Math.PI) / 180; //产生0~30之间的随机弧度 var txt = aCode[j]; //得到随机的一个内容 show_num[i] = txt.toLowerCase(); var x = 10 + i * 20; //文字在canvas上的x坐标 var y = 20 + Math.random() * 8; //文字在canvas上的y坐标 context.font = "bold 23px 微软雅黑"; context.translate(x, y); context.rotate(deg); context.fillStyle = this.randomColor(); context.fillText(txt, 0, 0); context.rotate(-deg); context.translate(-x, -y); } for (var i = 0; i <= 5; i++) { //验证码上显示线条 context.strokeStyle = this.randomColor(); context.beginPath(); context.moveTo( Math.random() * canvas_width, Math.random() * canvas_height ); context.lineTo( Math.random() * canvas_width, Math.random() * canvas_height ); context.stroke(); } for (var i = 0; i <= 30; i++) { //验证码上显示小点 context.strokeStyle = this.randomColor(); context.beginPath(); var x = Math.random() * canvas_width; var y = Math.random() * canvas_height; context.moveTo(x, y); context.lineTo(x + 1, y + 1); context.stroke(); } this.show_num = show_num; },
验证码及线条需要一些随机的颜色:
randomColor() { //得到随机的颜色值 var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return "rgb(" + r + "," + g + "," + b + ")"; }
有了以上两个方法,图形验证码就已经生成完毕了,接下来就是使用的问题了。
判断登录次数count
,如果登录次数大于0则需要输入验证码:
const self = this; if (this.count) { if (this.form.valiCode) { if (this.show_num.join("") != this.form.valiCode.toLowerCase()) { self.$Notice.warning({ title: "验证码错误" }); return; } } else { self.$Notice.warning({ title: "请输入验证码" }); return; } }
当登录失败时需要执行count++
并且刷新验证码:
self.count++; self.getImgYanzheng(); self.$Notice.warning({ title: "登陆失败", desc: rs.data.msg });
此时就完成了一个图形验证码的添加工作,同学们快装起来吧。
转评赞就是最大的鼓励