程式設計師不裝x能行?先給登入來一個圖形驗證碼!(canvas實現)

小熊我不要了發表於2019-09-09

細心的同學可以發現,現在很多網站當登入多次之後就會出現一個圖形驗證碼,或是當提交表單、或點選獲取手機驗證碼等等場景都會有圖形驗證碼的出現。

那麼圖形驗證碼是為了解決什麼問題而出現的呢?

什麼是圖形驗證碼

圖形驗證碼是驗證碼的一種。驗證碼(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繪圖

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
});複製程式碼

此時就完成了一個圖形驗證碼的新增工作,同學們快裝起來吧。file轉評贊就是最大的鼓勵關注公眾號,更多驚喜等著你

相關文章