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创建线程示例
May 06 Python
Python设计模式之职责链模式原理与用法实例分析
Jan 11 Python
python中PS 图像调整算法原理之亮度调整
Jun 28 Python
Python 用matplotlib画以时间日期为x轴的图像
Aug 06 Python
Python绘制热力图示例
Sep 27 Python
python运用sklearn实现KNN分类算法
Oct 16 Python
python实现加密的方式总结
Jan 19 Python
Python 基于FIR实现Hilbert滤波器求信号包络详解
Feb 26 Python
Python实现爬取网页中动态加载的数据
Aug 17 Python
python BeautifulSoup库的安装与使用
Dec 17 Python
python 使用Tensorflow训练BP神经网络实现鸢尾花分类
May 12 Python
python中使用 unittest.TestCase单元测试的用例详解
Aug 30 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
PHP4与PHP5的时间格式问题
2008/02/17 PHP
PHP在获取指定目录下的目录,在获取的目录下面再创建文件,多平台
2011/08/03 PHP
解析PHP中的file_get_contents获取远程页面乱码的问题
2013/06/25 PHP
5款适合PHP使用的HTML编辑器推荐
2015/07/03 PHP
Zend Framework开发入门经典教程
2016/03/23 PHP
Laravel 读取 config 下的数据方法
2019/10/13 PHP
Javascript客户端将指定区域导出到Word、Excel的代码
2008/10/22 Javascript
使用jQuery简化Ajax开发 Ajax开发入门
2009/10/14 Javascript
jQuery事件绑定和委托实例
2014/11/25 Javascript
jquery对dom节点的操作【推荐】
2016/04/15 Javascript
Vue声明式渲染详解
2017/05/17 Javascript
前端常见跨域解决方案(全)
2017/09/19 Javascript
js传递数组参数到后台controller的方法
2018/03/29 Javascript
关于HTML5的data-*自定义属性的总结
2018/05/05 Javascript
小程序自定义日历效果
2018/12/29 Javascript
浅谈layui框架自带分页和表格重载的接口解析问题
2019/09/11 Javascript
JavaScript计算正方形面积
2019/11/26 Javascript
JQuery常用选择器功能与用法实例分析
2019/12/23 jQuery
vue中jsonp插件的使用方法示例
2020/09/10 Javascript
Python的string模块中的Template类字符串模板用法
2016/06/27 Python
python3使用requests模块爬取页面内容的实战演练
2017/09/25 Python
pytorch torch.nn.AdaptiveAvgPool2d()自适应平均池化函数详解
2020/01/03 Python
Python 面向对象之类class和对象基本用法示例
2020/02/02 Python
pycharm实现在子类中添加一个父类没有的属性
2020/03/12 Python
学习Python需要哪些工具
2020/09/04 Python
adidas美国官网:adidas US
2016/09/21 全球购物
美国椅子和沙发制造商:La-Z-Boy
2020/10/25 全球购物
六查六看剖析材料
2014/02/15 职场文书
战略合作意向书范本
2014/04/01 职场文书
租房协议书范本
2014/04/09 职场文书
小学教师师德师风承诺书
2015/04/28 职场文书
发票退票证明
2015/06/24 职场文书
2016年植树节红领巾广播稿
2015/12/17 职场文书
餐饮行业关注的9大营销策略
2019/08/26 职场文书
vue使用v-model进行跨组件绑定的基本实现方法
2021/04/28 Vue.js
Go 语言结构实例分析
2021/07/04 Golang