Python 200行代码实现一个滑动验证码过程详解


Posted in Python onJuly 11, 2019

前言

做网络爬虫的同学肯定见过各种各样的验证码,比较高级的有滑动、点选等样式,看起来好像挺复杂的,但实际上它们的核心原理还是还是很清晰的,本文章大致说明下这些验证码的原理以及带大家实现一个滑动验证码。

实际上这类验证码的校验是分为两个步骤的:

1.第一步就是前端的校验。一般来说,登录注册页面在点击提交的时候都会伴随着一个表单提交,在表单提交的时候会有 JavaScript 事件的触发。如果加入了验证码,那么在表单提交的时候会多加一个额外的验证,判断这个验证码是否已经成功完成了操作。如果没有的话,那就直接取消表单的提交,然后顺便提示说”您的验证没通过,请重新验证“,诸如此类的话。所以这一步就能防范”君子“之为了。

2.第二步就是服务端的校验。意思就是说表单提交之后,会有请求发送到服务器,这个请求中包含了很多数据,比如用户名、密码,如果对接了验证码的话,还会有额外的验证码的值,或者更复杂的加密后的 Token 值,服务器会对发过来的信息进行校验,如果验证通过,那么整个请求就成功了,返回正常的响应,否则返回错误的响应。所以如果想要通过程序来直接构造表单提交的时候,服务端就可以做进一步的校验,由于提交的验证码相关的信息都是和服务端的 Session 相关联的,另外再加上一些 CSRF 等的校验,所以这一步就能防范”小人“之为了。

上面就是验证码校验的两个阶段,一般来说为了安全性,在开发一个网站时需要客户端和服务端都加上校验,这样才能保证安全性。

本文章主要来介绍一下第一个阶段,也就是前端校验的验证码的实现,下面来介绍一下拖动验证码的具体实现。

需求

那么前端完成一个合格的验证码,究竟需要做成什么样子呢?

1.首先验证码有个大体的雏形,既然是拖动验证码,那就要拖动块和目标块,我们需要把拖动块拖动到目标块上就算校验成功。

2.验证码的一个功能就是来规避机器的自动操作,所以我们需要通过轨迹来判断这个拖动过程是真实的人还是机器,因此我们需要记录拖动的路径,路径经过计算之后可以发送到后端进行进一步的分类,比如对接深度学习模型来分类拖动轨迹是否是人。

以上就是验证码的两个基本要求,所以我们这里就来实现一下看看。

结果

这里就先给大家看看结果吧:

Python 200行代码实现一个滑动验证码过程详解

拖动验证码示例

可以看到图中有一个初始滑块,有一个目标滑块,如果把初始滑块拖动到目标滑块上才能校验成功,然后下方再打印拖动的轨迹,包含它的 x、y 坐标。

有了这些内容之后,就可以放到表单里面进行提交了,轨迹数据可以自行加密处理并校验来判断其是否合法。

具体实现

下面就具体讲解下这个是怎么实现的,实际上核心代码只有 200 行,下面对整个核心流程进行说明。

既然 Vue 这么火,那我这里就用 Vue 来实现啦,具体的环境配置这里就不再赘述了,需要安装的有:

Node.js:https://nodejs.org/en/

Vue-Cli:https://cli.vuejs.org/zh/

安装完成之后便可以使用 vue 命令了,新建个项目:

vue create drag-captcha

然后找一张不错的风景图,放到 public 目录下,后面我们会引用它。

另外这里需要一个核心的包叫做 vue-drag-drop,其 GitHub 地址为:https://github.com/cameronhimself/vue-drag-drop,在目录下使用此命令安装:

npm install --save vue-drag-drop

安装好了之后我们就可以利用它来实现验证码了。

首先 vue-drag-drop 提供了两个组件,一个叫做 Drag,一个叫做 Drop。前者是被拖动对象,后者是放置目标,我们利用这两个组件构建两个滑块,将 Drag 滑块拖动到 Drop 滑块上就成功了。因此,我们要做的仅仅是把它们两个声明出来并添加几个检测方法就好了,至于拖动的功能,vue-drag-drop 这个组件已经给我们封装好了。

这里我们就直接在 App.vue 里面修改内容就好了,在 <template> 里面先声明一下两个组件:

<template>
 <div id="app">
 <div id="wrapper" :style="wrapperStyle">
 <drop class="drop" id="target"
 :class="{ 'over': state.over }"
 @dragover="onDragOver"
 @dragleave="onDragLeave"
 :style="targetStyle">
 </drop>
 <drag class="drag" id="source"
 :transfer-data="true"
 @dragstart="onDragStart"
 @dragend="onDragEnd"
 @drag="onDrag" v-if="!state.dragged"
 :style="sourceStyle">
 <div slot="image" id="float" :style="sourceStyle">
 </div>
 </drag>
 </div>
 </div>
</template>

很清晰了,一个 <drop> 和一个 <drag> 组件,里面绑定了一些属性,下面对这两个组件的常用属性作一下说明。

Drop

对于 Drop 组件来说,它是一个被放置的对象,被拖动滑块会放到这个 Drop 滑块上,这就代表拖动成功了。它有两个主要的事件需要监听,一个叫做 dragover,一个叫做 dragleave,分别用来监听 Drag 对象拖上和拖开的事件。

在这里,分别对两个事件设置了 onDragOver 和 onDragLeave 的回调函数,当 Drag 对象放到 Drop 对象上面的时候,就会触发 onDragOver 对象,当拖开的时候就会触发 onDragLeave 事件。

那这样的话我们只需要一个全局变量来记录是否已经将滑块拖动到目标位置即可,比如可以定一个全局变量 state,我们用 over 属性来代表是否拖动到目标位置。

因此 onDragOver 和 onDragLeave 事件可以这么实现:

onDragOver() {
 this.state.over = true
},
onDragLeave() {
 this.state.over = false
}

Drag

对于 Drag 组件来说,它是一个被拖动的对象,我们需要将这个 Drag 滑块拖动到 Drop 滑块上,就代表拖动成功了。它有三个主要的时间需要监听:dragstart、drag、dragend,分别代表拖动开始、拖动中、拖动结束三个事件,我们这里也分别设置了三个回调方法 onDragStart、onDrag、onDragEnd。

对于 onDragStart 方法来说,应该怎么实现呢?这里应该处理刚拖动的一瞬间的动作,由于我们需要记录拖动的轨迹,所以声明一个 trace 全局变量来保存轨迹信息,onDragStart 要做的就是初始化 trace 对象为空,另外记录一下初始的拖动位置,以便后续计算拖动路径,所以可以实现如下:

onDragStart(data, event) {
 this.init = {
 x: event.offsetX,
 y: event.offsetY,
 }
 this.trace = []
}

对于 onDrag 方法来说,就是处理拖动过程中的一系列拖动动作,这里其实就是计算当前拖动的偏移位置,然后把它保存到 trace 变量里面,所以可以实现如下:

onDrag(data, event) {
 let offsetX = event.offsetX - this.init.x
 let offsetY = event.offsetY - this.init.y
 this.trace.push({
 x: offsetX,
 y: offsetY,
 })
}

对于 onDragEnd 方法来说,其实就是检测最后的结果了,刚才我们用 state 变量里面的 over 属性来代表是否拖动到目标位置上,这里我们也定义了另外的 dragged 属性来代表是否已经拖动完成,dragging 属性来代表是否正在拖动,所以整个方法的逻辑上是检测 over 属性,然后对 dragging、dragged 属性做赋值,然后做一些相应的提示,实现如下:

onDragEnd() {
 if (this.state.over) {
 this.state.dragging = false
 this.state.dragged = true
 this.$message.success('拖动成功')
 }
 else {
 this.state.dragging = false
 this.state.dragged = false
 this.$message.error('拖动失败')
 }
 this.state.over = false
 }

OK 了,以上便是主要的逻辑实现,这样我们就可以完成拖动滑块的定义以及拖动的监听了。

接下来就是一些样式上的问题了,对于图片的呈现,这里直接使用 CSS 的 background-image 样式来设置的,如果想显示图片的某一个范围,那就用 background-position 来设置,这是几个核心的要点。

好,这里的样式设置其实也可以用 JavaScript 来实现,我们把它们定义为一些计算属性:

wrapperStyle() {
 return {
 width: this.size.width + 'px',
 height: this.size.height + 'px',
 backgroundImage: 'url(' + this.image + ')',
 backgroundSize: 'cover'
 }
},
targetStyle() {
 return {
 left: this.block.x + 'px',
 top: this.block.y + 'px'
 }
},
sourceStyle() {
 return {
 backgroundImage: 'url(' + this.image + ')',
 backgroundSize: this.size.width + 'px ' + this.size.height + 'px',
 backgroundPosition: -this.block.x + 'px ' + -this.block.y + 'px'
 }
}

另外这里还有一个值得注意的地方,就是 Drag 组件的 slot 部分:

<div slot="image" id="float" :style="sourceStyle"></div>

这部分定义了在拖动过程中随鼠标移动的图片样式,这里也和 Drag 滑块一样定义了一样的样式,这样在拖动的过程中,就会显示一个和 Drag 滑块一样的滑块随鼠标移动。

最后,就是拖拽完成之后,将滑动轨迹输出出来,这里我就直接呈现在页面上了,<template> 区域加入如下定义即可:

<div>
 <p v-if="state.dragged" id="trace">
 拖动轨迹:{{ trace }}
 </p>
</div>

好,以上就是一些核心代码的介绍,还有一些细节的问题可以完善下,比如滑块随机初始化位置,以及拖动样式设置。

最后再看一遍效果:

Python 200行代码实现一个滑动验证码过程详解

拖动验证码示例

可以看到我们首先拖动了 Drag 滑块,当 Drag 滑块拖动到 Drop 滑块上时,出现了白色描边,证明已经拖动到目标位置了。然后松手之后,触发 onDragEnd 方法,呈现拖动轨迹,整个验证码就验证成功了。

当然这只是前端部分,如果在这个基础上添加表单验证,然后再添加后端校验,并加上轨迹识别,那可谓是一个完整的验证码系统了,在这里就点到为止啦。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python爬虫教程之爬取百度贴吧并下载的示例
Mar 07 Python
pycharm 使用心得(四)显示行号
Jun 05 Python
python制作最美应用的爬虫
Oct 28 Python
python实现本地图片转存并重命名的示例代码
Oct 27 Python
Python寻找两个有序数组的中位数实例详解
Dec 05 Python
对json字符串与python字符串的不同之处详解
Dec 19 Python
浅谈Python 多进程默认不能共享全局变量的问题
Jan 11 Python
python使用正则筛选信用卡
Jan 27 Python
Python3使用TCP编写一个简易的文件下载器功能
May 08 Python
Python使用pyserial进行串口通信的实例
Jul 02 Python
Python 使用matplotlib模块模拟掷骰子
Aug 08 Python
Python制作表白爱心合集
Jan 22 Python
ML神器:sklearn的快速使用及入门
Jul 11 #Python
python 随机森林算法及其优化详解
Jul 11 #Python
python实现从本地摄像头和网络摄像头截取图片功能
Jul 11 #Python
python常用库之NumPy和sklearn入门
Jul 11 #Python
python在新的图片窗口显示图片(图像)的方法
Jul 11 #Python
Python实现K折交叉验证法的方法步骤
Jul 11 #Python
Python获取命令实时输出-原样彩色输出并返回输出结果的示例
Jul 11 #Python
You might like
php实现用户在线时间统计详解
2011/10/08 PHP
php操作JSON格式数据的实现代码
2011/12/24 PHP
php5.3提示Function ereg() is deprecated Error问题解决方法
2014/11/12 PHP
详解WordPress开发中过滤属性以及Sql语句的函数使用
2015/12/25 PHP
PHP获取昨天、今天及明天日期的方法
2016/02/03 PHP
php实现的生成排列算法示例
2019/07/25 PHP
JQuery FlexiGrid的asp.net完美解决方案 dotNetFlexGrid-.Net原生的异步表格控件
2010/09/12 Javascript
JS高级笔记
2011/07/13 Javascript
查询json的数据结构的8种方式简介
2014/03/10 Javascript
jQuery插件Timelinr 实现时间轴特效
2015/10/04 Javascript
vuex与组件联合使用的方法
2018/05/10 Javascript
微信小程序开发之自定义tabBar的实现
2018/09/06 Javascript
javascriptvoid(0)含义以及与&quot;#&quot;的区别讲解
2019/01/19 Javascript
JavaScript函数Call、Apply原理实例解析
2020/02/17 Javascript
vue实现数字滚动效果
2020/06/29 Javascript
如何使用gpu.js改善JavaScript的性能
2020/12/01 Javascript
django实现分页的方法
2015/05/26 Python
Python判断文件或文件夹是否存在的三种方法
2017/07/27 Python
详解 Python 读写XML文件的实例
2017/08/02 Python
详解Python在七牛云平台的应用(一)
2017/12/05 Python
python模拟表单提交登录图书馆
2018/04/27 Python
多版本python的pip 升级后, pip2 pip3 与python版本失配解决方法
2019/09/11 Python
django框架ModelForm组件用法详解
2019/12/11 Python
Win 10下Anaconda虚拟环境的教程
2020/05/18 Python
python实现学生信息管理系统(精简版)
2020/11/27 Python
HTML5实现一个能够移动的小坦克示例代码
2013/09/02 HTML / CSS
英语自荐信范文
2013/12/11 职场文书
法学专业毕业生自荐信范文
2013/12/18 职场文书
秋季运动会活动方案
2014/02/05 职场文书
项目工作说明书
2014/07/29 职场文书
主要领导对照检查材料
2014/08/26 职场文书
委托培训协议书
2014/11/17 职场文书
人口与计划生育责任书
2015/05/09 职场文书
春晚观后感
2015/06/11 职场文书
Django框架中模型的用法
2022/06/10 Python
MySQL分布式恢复进阶
2022/07/23 MySQL