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内存管理分析
Apr 08 Python
Python使用multiprocessing创建进程的方法
Jun 04 Python
Python手机号码归属地查询代码
May 04 Python
Python cookbook(数据结构与算法)从序列中移除重复项且保持元素间顺序不变的方法
Mar 13 Python
分享Python切分字符串的一个不错方法
Dec 14 Python
python实现截取屏幕保存文件,删除N天前截图的例子
Aug 27 Python
基于Django统计博客文章阅读量
Oct 29 Python
Python实现FLV视频拼接功能
Jan 21 Python
Python用input输入列表的实例代码
Feb 07 Python
Python3使用腾讯云文字识别(腾讯OCR)提取图片中的文字内容实例详解
Feb 18 Python
Python编程根据字典列表相同键的值进行合并
Oct 05 Python
Python机器学习应用之基于线性判别模型的分类篇详解
Jan 18 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
真正面向对象编程:PHP5.01发布
2006/10/09 PHP
一个数据采集类
2007/02/14 PHP
php 随机生成10位字符代码
2009/03/26 PHP
Ext.data.PagingMemoryProxy分页一次性读取数据的实现代码
2010/04/07 PHP
php把数据表导出为Excel表的最简单、最快的方法(不用插件)
2014/05/10 PHP
PHP中一些可以替代正则表达式函数的字符串操作函数
2014/11/17 PHP
PHP中使用register_shutdown_function函数截获fatal error示例
2015/04/21 PHP
PHP数组编码gbk与utf8互相转换的两种方法
2016/09/01 PHP
Laravel框架定时任务2种实现方式示例
2018/12/08 PHP
JQuery Dialog的内存泄露问题解决方法
2010/06/18 Javascript
js的回调函数详解
2015/01/05 Javascript
Linux下为Node.js程序配置MySQL或Oracle数据库的方法
2016/03/19 Javascript
jQuery实现的鼠标经过时变宽的效果(附demo源码)
2016/04/28 Javascript
Vue通过input筛选数据
2020/10/26 Javascript
浅谈ES6 模板字符串的具体使用方法
2017/11/07 Javascript
vue数据传递--我有特殊的实现技巧
2018/03/20 Javascript
在vue中实现点击选择框阻止弹出层消失的方法
2018/09/15 Javascript
vue动画效果实现方法示例
2019/03/18 Javascript
Vue中util的工具函数实例详解
2019/07/08 Javascript
jQuery开发仿QQ版音乐播放器
2020/07/10 jQuery
js实现弹幕墙效果
2020/12/10 Javascript
[03:39]DOTA2英雄梦之声_第05期_幽鬼
2014/06/23 DOTA
python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解
2017/11/24 Python
Python实现的求解最小公倍数算法示例
2018/05/03 Python
python:动态路由的Flask程序代码
2019/11/22 Python
python 解决selenium 中的 .clear()方法失效问题
2020/09/01 Python
用CSS3写的模仿iPhone中的返回按钮
2015/04/04 HTML / CSS
Java里面如何创建一个内部类的实例
2015/01/19 面试题
什么是Rollback Segment
2013/04/22 面试题
简述使用ftp进行文件传输时的两种登录方式?它们的区别是什么?常用的ftp文件传输命令是什么?
2016/11/20 面试题
东方红海科技面试题软件测试方面
2012/02/08 面试题
优秀教导主任事迹材料
2014/05/09 职场文书
齐云山导游词
2015/02/06 职场文书
python基础之模块的导入
2021/10/24 Python
国际最新研究在陨石中发现DNA主要成分 或由陨石带来地球
2022/04/29 数码科技