当前位置: 首页 > 后端技术 > Node.js

Koa从零开始构建验证码

时间:2023-04-03 20:31:44 Node.js

前言目前我们可以接收到的验证码包括短信/邮件-使用数字/字符串的随机组合,一般长度为4~6个识别图片-字符串/计算滑块-拼图选择-点击目标文字/图片,按顺序点击等。准备工作接下来,我们将使用Koa来实现上述集中验证码。在此之前,我们需要安装一些插件。-图片处理ImageMagick-图片处理nodemailer-发邮件和图片识别的验证码有很多,比如captchapngccaptrek-captcha基本功能不一样,大家可以自己试试。由于本文需要和前端交互,所以还需要koa2-cors来解决跨域。实现跨域在我们开始写代码之前,当然要解决跨域问题。在app.js中写入配置constcors=require('koa2-cors')app.use(cors({origin:function(ctx){return'*';},exposeHeaders:['WWW-Authenticate','Server-Authorization'],maxAge:5,credentials:true,allowMethods:['GET','POST','DELETE'],allowHeaders:['Content-Type','Authorization','Accept'],}))Randomcharacters随机字符,很简单,我们使用svg-captcha随机生成字符和图片,将图片返回给前端,服务器端访问它们对应的值进行验证。直接上代码constsvgCaptcha=require('svg-captcha')constgetString=async()=>{constcap=svgCaptcha.create({size:4,//验证码长度width:160,height:60,fontSize:50,ignoreChars:'0oO1ilI',//验证码字符排除0o1inoise:2,//干扰线数color:true,//验证码字符是否有颜色,默认没有,如果设置了背景,则默认有背景:'#eee'//验证码图像背景色})letimg=cap.data//验证码vartext=cap.text.toLowerCase()//验证码字符,忽略大小写return{svg:`${img}${text}`}}我们会将“svg”返回给客户端,直接渲染即可。计算类也使用svg-captcha实现constsvgCaptcha=require('svg-captcha')constgetNumber=async()=>{constcap=svgCaptcha.createMathExpr({size:4,//验证码长度width:160,height:60,fontSize:50,ignoreChars:'0oO1ilI',//排除验证码字符中的0o1inoise:2,//干扰线数color:true,//验证码字符是否有颜色,默认无,如果设置了背景,则默认为背景:'#eee'//验证码图像背景颜色})letimg=cap.data//验证码vartext=cap.text.toLowerCase()//验证码字符,忽略大小写return{svg:`${img}${text}`}}同上。滑动滑动模块的实现比较复杂,但是它的逻辑还是很简单的。我们先梳理一下实现逻辑。服务器:一张大图,有50*50px的缺块和50*50px的图片补缺。大图空位坐标(随机生成)服务端生成以上信息返回给客户端(坐标值返回y)。客户端:渲染大图并初始化小图的位置(y已知,left:0)。滑动完成后,将x坐标发送给服务器进行校验。图像生成constgm=require('gm').subClass({imageMagick:true});vararrBuffer=[]constgetSlide=async()=>{arrBuffer=[]constwidth=420constheight=250constfragmentSize=50try{//生成图像constfilePath=getRandomPath()constx=(Math.floor(Math.random()*1000)%(width-2*fragmentSize))+fragmentSizeconsty=Math.floor(Math.random()*1000)%(height-fragmentSize)const{image,fragment}=awaitcreateImage(filePath,width,height,fragmentSize,x,y)//缓存记录arrBuffer.push({x})console.log(arrBuffer)return{msg:"ok",data:{image,fragment,y}}}catch(err){return{msg:"ServerError:"+err,data:null}}}函数getRandomPath(){constfileLength=4constindex=Math.floor(Math.random()*1000)%fileLengthreturnpath.resolve(__dirname,`../static/images/${index+1}.jpg`)}函数createImage(filePath,w,h,s,x,y){returnnewPromise((resolve,reject)=>{constres={image:"",fragment:""}gm(filePath).resize(w,h,"!").fill("rgba(0,0,0,.5)")//绘制一个由坐标对、宽度和高度指定的矩形。drawRectangle(x,y,x+s-1,y+s-1).noProfile().setFormat('jpeg').toBuffer((err,buffer)=>{if(err){reject(err)}res.image="data:image/jpg;base64,"+buffer.toString("base64")gm(filePath).resize(w,h,"!").crop(s,s,x,y).??noProfile().setFormat('jpeg').toBuffer((err,buffer)=>{if(err){拒绝(err)}res.fragment="data:image/jpg;base64,"+buffer.toString("base64")resolve(res)})})})}这样我们就可以得到两张图片及其坐标.大图和小图的前端可以根据两张图和y坐标实现滑块的初始滑动过程。相信大家都能写出来!此处不详尽描述。当我们听完幻灯片后,会将缩略图clienX值的记录返回给服务端,与服务端缓存的X坐标进行匹配。然后我们实现Check接口constcheck=async(data)=>{const{x}=dataconstisMatch=Math.abs(x-arrBuffer[0].x)<5if(isMatch){return{success:true,msg:'验证成功,99.9%以上用户'}}else{return{success:false,msg:'验证失败'}}}email邮箱验证码难点在于发邮件。我们只需要将随机生成的字符串通过邮件发送给目标客户端即可,短信也是如此。让我们实现一个简单的电子邮件发送接口。constnodemailer=require('nodemailer')constsmtpConfig={host:'smtp.163.com',port:465,secure:true,auth:{user:'',pass:''}}constmail=async(data)=>{vartransporter=nodemailer.createTransport(smtpConfig);letmailOptions={from:'',to:'',subject:'验证码',text:Math.random().toString(36).substr(2,4)}传输器。sendMail(mailOptions,(error,info)=>{if(error){returnconsole.log(error);}console.log(info)})return{success:true}}