效果图:(如图所示)

在这里插入图片描述

项目背景:

  • 之前就没有规划说有验证码的需求,产品又要硬加
  • 后端也没得时间写接口,就叫我自己搞,不要烦他,他算数据心情不好
  • 😒 哎,前端,dddd

开始调研:

无奈,开始百度cv大法,找了一圈,什么verify啊,jQuery的啊,原生的啊五花八门
看不懂,然后用在自己的项目又不友好,老是一堆error,而且引入的文件太多了,一点都不划算,怪自己太菜了,也不想再麻烦了
于是,只能自己写了,当然也是借鉴了一下别人的代码,然后变成了自己的

【首先,当然了,要搞清楚为啥这样做】

前端验证和后端验证区别:

前端

说法一:
验证码是防机器的,防止恶意破解密码、刷票、论坛灌水、刷页。有效的防止某个黑客以特定程序暴力破解的方式进行不断的登录尝试。
一种常用的CAPTCHA测试是让用户输入一个扭曲变形的图片上所显示的文字或数字,扭曲变形是为了避免被光学字符识别(OCR, Optical Character Recognition)之类的电脑程序自动辨识出图片上的文数字而失去效果。由于这个测试是由计算机来考人类,而不是标准图灵测试中那样由人类来考计算机,人们有时称CAPTCHA是一种反向图灵测试,

说法二:
首先,验证码的基本功能是进行人机识别,拦截机器行为。可以稍微细想一下,如果你们使用的方式,自己都很容易就能抓取并识别,那就应该避免使用这种方式了。而且,现在图像识别技术也已经很先进了,再加上打码平台的存在,单纯依靠验证码并不安全。如果对安全性有要求,同时也考虑用户体验的话,建议使用行为验证,我们平时看到比较多的是滑动验证。行为验证是利用生物行为特征模型进行人机识别,用在注册登录、防刷红包优惠券、防薅羊毛、数据反爬等各种调用接口的场景。顶象的行为验证技术已经结合了设备指纹信息、IP/手机号风险、访问频率、地理位置等等的多维度信息,判断更加精准。

后端

后端验证码是由后端生成的验证码。
当用户打开登录页面后,浏览器会向服务器发送请求并携带生成的令牌tolken,服务器随机生成验证码,并将验证码和tolken的对应关系存储在redis缓冲中,之后会在前端动态的生成一张验证码图片。
当用户输入验证码并点击登录的时候,服务器会在redsi缓存中找到该浏览器的tolken对应的验证码,验证验证码是否正确,如果正确,接下来开始比较用户名和密码。

功能原理分析:

  1. 如何实现验证码的随机性?
  2. 如何实现一块区域来展示验证码?
  3. 输入框的值和验证码值之间如何建立联系?
  4. 如何美化验证码UI,至少看起来得像吧

OK,带着这几个疑问,能搞清楚,基本就可以实现了
1----需要大量使用Math 方法
2----受到 echarts 和 百度地图这些 canvas库的启发,决定用canvas来实现,这样好理解,而且数据也好对应(关键是我只会canvas😩)
3----如果是Vue的话,就很简单,下面会详细说明, 如果是原生或者jQuery的话,可能在存验证码那里不像Vue那样优雅,仅此而已
4----我看了下别人写的验证码,至少得有 “倾斜”,“多余的线和点” 这几样,反正就是让你要仔细看才能看清楚的那种感觉,哈哈

实现代码:

HTML

<div>
   <span class="login">验证:</span>
   <input
     style="width: 100px"
     type="text"
     placeholder="请输入验证码"
     class="input-val"
     v-model.trim="yanzhen"
     @keyup.enter="login_enter"
   />
   <canvas id="canvas" width="130px" height="33px" @click="handleCanvas"> </canvas>
 </div>

CSS

#canvas {
  float: right;
  margin-right: 1%;
  display: block;
  border: 1px solid #ccc;
  border-radius: 5px;
  cursor: pointer;
  margin-right: 80px;
}

.input-val {
  width: 50%;
  background: #ffffff;
  height: 2.8rem;
  border-radius: 5px;
  border: none;
  padding: 0 0 0 12px;
  border: 1px solid rgba(0, 0, 0, 0.2);
}

JS

data() {
    return {
      //20211112
      yanzhen: "",//输入框双向绑定的值
      true_code: "",//保存正确的验证码
      yanzhen_arr:[],//只用于传参,并且数组长度不能「多于」下面验证码遍历的次数,不然最终得到的true_code会有问题 
      //比如下面是4个验证码,可以是[1,2,3,4]及以下,但是不能是[1,2,3,4,5], 因为5无法被替换
      
    };
  },

mounted() {
    this.draw(this.yanzhen_arr)
  },
methods(){
//20211115 新增验证码功能
    //显示验证码区域内容信息
    draw(show_num) {
      var canvas_width =  document.querySelector("#canvas").clientWidth;
      var canvas_height = document.querySelector("#canvas").clientHeight;
      var canvas = document.getElementById("canvas"); //获取到canvas
      var context = canvas.getContext("2d"); //获取到canvas画图
      canvas.width = canvas_width;
      canvas.height = canvas_height;
      var sCode =
        "a,b,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,E,F,G,H,J,K,L,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; //获取到数组的长度

      //4个验证码数
      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);
      }
      //验证码上显示6条线条
      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();
      }
      //验证码上显示31个小点
      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();
      }

      //最后把取得的验证码数组存起来,方式不唯一
      var num = show_num.join("");
      // console.log(num);
      this.true_code = 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 + ")";
    },
    //canvas点击刷新
    handleCanvas(){
      this.draw(this.yanzhen_arr);
    },
}

说明:

这是整个基本功能的实现,下面就是if判断的事了,就不贴了,
特别说明下,原来这段代码是用jQuery写的,但是我不想在Vue里面用jQuery了,所以就微调了一下代码
认真看都能看得懂的,就不一一解释了

bug整理:

最开始存数据的时候用的window对象来存的,出了一个面试常问的关于计算属性和函数的bug,在此贴图记录一下

如图:

在这里插入图片描述
在这里插入图片描述

感谢浏览,
水平有限,一起进步!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐