1. 程式人生 > >實現圖片滑動驗證碼js外掛+後臺(完整版)

實現圖片滑動驗證碼js外掛+後臺(完整版)

之前一直比較忙,最近也看到之前發的很多人問,現在我就提取出來發個簡化完整版的。

我後端採用python+flask+redis,簡易實現圖片驗證功能,如需複雜驗證,請記錄滑鼠拖動軌跡,後端實現分析,具體有興趣的朋友可以試試。

js外掛重要程式碼

/**
 * Created by lgy on 2017/10/21.
 * 圖片驗證碼
 */
(function ($) {
    $.fn.imgcode = function (options) {
        //初始化引數
        var defaults = {
            frontimg:"",
            backimg:"",
            refreshImg:"",
            getsuccess:"",
            getfail:"",
            maskclose:true,
            callback:"",  //回撥函式
            refreshcallback:""
        };
        var opts = $.extend(defaults, options);
        return this.each(function () {
            var $this = $(this);//獲取當前物件
            var html = '<div class="code-k-div">' +
                '<div class="code_bg"></div>' +
                '<div class="code-con">' +
                '<div class="code-img">' +
                '<div class="code-img-con">' +
                '<div class="code-mask"><img class="code-front-img" src="'+opts.frontimg+'"></div>' +
                '<img class="code-back-img" src="'+opts.backimg+'"></div>' +
                '<div class="code-push"><i class="icon-login-bg icon-w-25 icon-push">重新整理</i><span class="code-tip"></span></div>' +
                '</div>' +
                '<div class="code-btn">' +
                '<div class="code-btn-img code-btn-m"></div>' +
                '<span class="code-span">按住滑塊,拖動完成上方拼圖</span>' +
                '</div></div></div>';
            $this.html(html);

            //定義拖動引數
            var $divMove = $(this).find(".code-btn-img"); //拖動按鈕
            var $divWrap = $(this).find(".code-btn");//滑鼠可拖拽區域
            var mX = 0, mY = 0;//定義滑鼠X軸Y軸
            var dX = 0, dY = 0;//定義滑動區域左、上位置
            var isDown = false;//mousedown標記
            if(document.attachEvent) {//ie的事件監聽,拖拽div時禁止選中內容,firefox與chrome已在css中設定過-moz-user-select: none; -webkit-user-select: none;
                $divMove[0].attachEvent('onselectstart', function() {
                    return false;
                });
            }
            //按鈕拖動事件
            $divMove.unbind('mousedown').on({
                mousedown: function (e) {
                    //清除提示資訊
                    $this.find(".code-tip").html("");
                    var event = e || window.event;
                    mX = event.pageX;
                    dX = $divWrap.offset().left;
                    dY = $divWrap.offset().top;
                    isDown = true;//滑鼠拖拽啟
                    $(this).addClass("active");
                    //修改按鈕陰影
                    $divMove.css({"box-shadow":"0 0 8px #666"});
                }
            });
            //點選背景關閉
            if(opts.maskclose){
                $this.find(".code_bg").unbind('click').click(function(){
                    $this.html("");
                })
            }
            //重新整理code碼
            $this.find(".icon-push").unbind('click').click(function(){
                opts.refreshcallback();
            });
            //滑鼠點選鬆手事件
            $divMove.unbind('mouseup').mouseup(function (e) {
                var lastX = $this.find(".code-mask").offset().left - dX - 1;
                isDown = false;//滑鼠拖拽啟
                $divMove.removeClass("active");
                //還原按鈕陰影
                $divMove.css({"box-shadow":"0 0 3px #ccc"});
                returncode(lastX);
            });
            //滑動事件
            $divWrap.mousemove(function (event) {
                var event = event || window.event;
                var x = event.pageX;//滑鼠滑動時的X軸
                if (isDown) {
                    if(x>(dX+30) && x<dX+$(this).width()-20){
                        $divMove.css({"left": (x - dX - 20) + "px"});//div動態位置賦值
                        $this.find(".code-mask").css({"left": (x - dX-30) + "px"});
                    }
                }
            });
            //返回座標系
            function returncode(xpos){
                opts.callback({xpos:xpos});
            }
            //驗證資料
            function checkcode(code){
                var iscur=true;
                //模擬ajax
                setTimeout(function(){
                    if(iscur){
                        checkcoderesult(1,"驗證通過");
                        $this.find(".code-k-div").remove();
                        opts.callback({code:1000,msg:"驗證通過",msgcode:"23dfdf123"});
                    }else{
                        $divMove.addClass("error");
                        checkcoderesult(0,"驗證不通過");
                        opts.callback({code:1001,msg:"驗證不通過"});
                        setTimeout(function() {
                            $divMove.removeClass("error");
                            $this.find(".code-mask").animate({"left":"0px"},200);
                            $divMove.animate({"left": "10px"},200);
                        },300);
                    }
                },500)
            }
            //重新整理圖示
            opts.refreshImg=function(data){
                console.log(data)
                $this.find(".code-img-con .code-front-img").attr("src",data.frontImg);
                $this.find(".code-img-con .code-back-img").attr("src",data.backGoundImg);
            }
            //驗證成功
            opts.getsuccess=function(){
                checkcoderesult(1,"驗證通過");
                setTimeout(function() {
                    $this.find(".code-k-div").remove();
                },800);

            }
            //驗證失敗
            opts.getfail=function(txt){
                $divMove.addClass("error");
                checkcoderesult(0,txt);
                setTimeout(function() {
                    $divMove.removeClass("error");
                    $this.find(".code-mask").animate({"left":"0px"},200);
                    $divMove.animate({"left": "10px"},200);
                },400);
            }
            //驗證結果
            function checkcoderesult(i,txt){
                if(i==0){
                    $this.find(".code-tip").addClass("code-tip-red");
                }else{
                    $this.find(".code-tip").addClass("code-tip-green");
                }
                $this.find(".code-tip").html(txt);
            }
        })
    }
})(jQuery);

python實現介面重要程式碼

rHost='127.0.0.1', rPort=6379, rDb=1, rPasswd='123456' 這裡是配置redis的地方。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from PIL import Image, ImageFilter
import random
import glob
# from matplotlib.pyplot import imsave
import base64
from io import BytesIO
import redis,os

backgroundPaton = os.getcwd()+"/app/codeimg/*.[pP][nN][gG]"
maskPaton = os.getcwd()+"/app/codeimgMask/*[0-9]-[bB].[pP][nN][gG]"
yMargin = 2
kSuffix = "_captchaCode"

class captcha:
    def __init__(self, rHost='127.0.0.1', rPort=6379, rDb=1, rPasswd='123456'):
        self.host = rHost
        self.port = rPort
        self.db = rDb
        self.passwd = rPasswd
        # self.r = rds

    def getRedis(self):
        pool = redis.ConnectionPool(host=self.host, port=self.port, db=self.db, password=self.passwd)
        return redis.StrictRedis(connection_pool=pool)
        # return redis.Redis(host=self.host, port=self.port,db=self.db,password=self.passwd)

    def getCode(self, did):
        if (len(did) < 32):
            return 0, 0

        bImgFile = random.choice(glob.glob(backgroundPaton))  # 隨機取底圖檔案
        bBorderFile = random.choice(glob.glob(maskPaton))  # 隨機取底圖蒙層檔案
        # print(bBorderFile)
        fBorderPaton = bBorderFile[:(len(bBorderFile) - 5)] + "[wW].[pP][nN][gG]"  #
        fBorderFile = random.choice(glob.glob(fBorderPaton))  # 上層圖蒙層檔案

        # print(fBorderFile)
        bImg = Image.open(bImgFile)  # 底圖
        bBorder = Image.open(bBorderFile)  # 底圖蒙層
        fBorder = Image.open(fBorderFile)  # 上層圖蒙層

        xPos = random.randint(bBorder.size[0], bImg.size[0] - bBorder.size[0])  # 取圖x座標
        yPos = random.randint(yMargin, bImg.size[1] - bBorder.size[1] - yMargin)  # 取圖y座標
        pos = (xPos, yPos)
        reg = pos + (xPos + bBorder.size[0], yPos + bBorder.size[1])  # 取圖矩形區域

        cImg = bImg.crop(reg)  # 底圖中取矩形圖
        bMask = fBorder.convert('RGBA')  # 蒙層邊界
        fChip = Image.composite(cImg, bMask, mask=fBorder)  # 取出圖片合成外層邊框
        fImg = Image.new("RGBA", bImg.size)
        fImg.paste(fChip, (0, yPos))
        #fImg.save("front.png")
        fImgBuf = BytesIO()
        fImg.save(fImgBuf, format="png")

        bMask = bBorder.convert('RGBA')  # 蒙層邊界
        bImg.paste(bBorder, pos, mask=bMask)  # 疊加蒙層
        #bImg.save("back.png")
        bImgBuf = BytesIO()
        bImg.save(bImgBuf, format="png")

        rds = self.getRedis()
        rds.set(did + kSuffix, xPos, ex=60)
        self.fImgStream = 'data:image/png;base64,'+base64.b64encode(fImgBuf.getbuffer()).decode()
        self.bImgStream = 'data:image/png;base64,'+base64.b64encode(bImgBuf.getbuffer()).decode()
        # return self.fImgStream,self.bImgStream
        return {"code":200,"frontImg": self.fImgStream, "backGoundImg": self.bImgStream}

    #獲取通過碼狀態
    def getCodeState(self,did):
        rdsKeyResult = did + "_captchaResult"
        rds = self.getRedis()
        xValue=False
        try:
            xValue = rds.get(rdsKeyResult)
            xValue=bool(xValue)
            rds.delete(rdsKeyResult)
        except:
            print("獲取失敗")
        return xValue

    def chkCode(self, did, xPos, off=2):  # 1-驗證成功;2-驗證失敗;3-驗證碼過期;4-驗證次數超過3次,off-誤差值
        rdsKey = did + kSuffix
        rdsChkKey = did + "_captchaCnt"
        rdsKeyResult = did + "_captchaResult"
        rds = self.getRedis()
        try:
            xValue = rds.get(rdsKey)
            xPosRDS = int(xValue)
        except:
            rds.delete(rdsKey)
            rds.delete(rdsChkKey)
            return 3
        rdsChkCount = rds.incr(rdsChkKey)
        if (rdsChkCount > 3):
            rds.delete(rdsKey)
            rds.delete(rdsChkKey)
            return 4
        if (abs(xPosRDS - xPos) <= off):
            rds.delete(rdsKey)
            rds.delete(rdsChkKey)
            rds.set(rdsKeyResult, True, ex=30)
            return 1
        else:
            return 2

    # 生成手機號驗證碼
    def setSmsCode(self, did):
        rdsKeyResult = did + "_captchaMobile"
        rds = self.getRedis()
        randomnum=random.randint(10000,999999)
        try:
            rds.set(rdsKeyResult, randomnum, ex=120)
            result = {"code": 200, "smscode": randomnum}
        except:
            result = {"code": 203, "smscode":0}
        return result

    # 檢測簡訊驗證碼
    def chkSmsCode(self, did,smscode):
        rdsKeyResult = did + "_captchaMobile"
        rds = self.getRedis()
        xValue = False
        try:
            xall = rds.get(rdsKeyResult)
            xall = int(xall)
            if(int(smscode)==xall):
                rds.delete(rdsKeyResult)
                xValue=True
        except:
            print("驗證失敗")
        return xValue

if __name__=='__main__':
    myCaptcha = captcha()
    print(myCaptcha.getCode('abcdefghijklmnopqrstuvwxdefghi3d'))
    #print(myCaptcha.chkCode('abcdefghijklmnopqrstuvwxdefghi3d', 182))

例項展示: http://www.xiuler.com        (可輸入32位字元測試,例:OtgsFq6d4mv7sEox4b2wBTdRtIBCSug8)