實現圖片滑動驗證碼js外掛+後臺(完整版)
阿新 • • 發佈:2018-12-20
之前一直比較忙,最近也看到之前發的很多人問,現在我就提取出來發個簡化完整版的。
我後端採用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)