博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
clicaptcha中文点击验证码开发经验总结
阅读量:6215 次
发布时间:2019-06-21

本文共 5262 字,大约阅读时间需要 17 分钟。

 现在的验证码真是越来越高级了,12306 的找图验证码,极验的拖动式验证码,还有国外的一些黑科技,能智能判断你是不是机器人的验证码。

  验证码的更新迭代让我突然对传统验证码一下子不满足了,出于挑战自我和对自己技能的修炼,我用了一周的时间写了一个简单的 demo ,然后又花了一周时间将其优化成插件的形式,于是  就诞生了。

  简单介绍下 Clicaptcha ,它是由 click 和 captcha 这两个单子合并而成,顾名思义,这是一个点击验证码,那怎么个点击验证呢?整个操作流程只需根据提示文字信息,点击图中文字所在位置,即可完成验证,效果图下图:

  具体的功能实现这里就不一步步给大家回顾了,感兴趣的可以直接上 oschina 或者 github ,搜索  就可以看到这个项目。

  下面我主要是记录一下在开发过程中几个难点,以及我的解决思路,如果你有更好的,希望你能和我交流交流。

  难点一:文字随机布局

  首先我们要做一些准备工作:

  1. 背景图片
  2. 中文字体
  3. 随机文字
  4. 字体所占范围(因为是 php 生成,所以借助 GD 库里的 imagettfbbox 方法)

  准备好这些后,就可以开始考虑我们的随机布局算法了,其实并不复杂,如果有看过我之前写的《》,其实思路是差不多的。

  可以看下上面这张图,假设中间带背景色的区域是已经固定的一个区域,当第2个区域要进行随机生成的时候,大概会有4种情况,也就是图中的这4种,我们只需依次判断以下4种条件,只要有一项符合,则这个随机生成的x,y坐标就可以使用。

x2 + w2 < x1

x1 + w1 < x2

y2 + h2 < y1

y1 + h1 < y2

  难点二:字体大小有偏差

  其实这倒不算个难点,就是个小细节。

  GD 库里的 imagefttext 方法中,设置字体大小并不是以像素(px)为单位的,而是以磅(point)为单位。所以在具体使用的时候,需要进行转换,也就是乘以 0.75 ,比如你需要在图片上展示 50px 大小的字体,则需要 50px * 0.75 = 37.5point 。至于为什么是乘以 0.75 ,可以见下表:

八号 = 5磅(7px) = (5/72)*96 = 6.67 = 6px

七号 = 5.5磅 = (5.5/72)*96 = 7.3 = 7px

小六 = 6.5磅 = (6.5/72)*96 = 8.67 = 8px

六号 = 7.5磅 = (7.5/72)*96 = 10px

小五 = 9磅 = (9/72)*96 = 12px

五号 = 10.5磅 = (10.5/72)*96 = 14px

小四 = 12磅 = (12/72)*96 = 16px

四号 = 14磅 = (14/72)*96 = 18.67 = 18px

小三 = 15磅 = (15/72)*96 = 20px

三号 = 16磅 = (16/72)*96 = 21.3 = 21px

小二 = 18磅 = (18/72)*96 = 24px

二号 = 22磅 = (22/72)*96 = 29.3 = 29px

小一 = 24磅 = (24/72)*96 = 32px

一号 = 26磅 = (26/72)*96 = 34.67 = 34px

小初 = 36磅 = (36/72)*96 = 48px

初号 = 42磅 = (42/72)*96 = 56px

  难点三:如何将图片和文字同时输出

  我们都知道,在 PHP 中,通过设置 header 的参数,可以输出各种文件类型,但一次只能输出一种数据格式到客户端。

  在我这个项目中,除了图片需要输出,同时还需要将提示文字也输出,不然用户就不知道依次点哪些文字进行验证了。

  解决这个问题我想到有两种解决方案:

  1. 将图片保存,把图片地址和提示文字一并输出到前端
  2. 只输出图片到前端,同时将提示文件放入 cookie 中,前端调取 cookie 显示提示文字

  最终我是选择了第二套方案,因为这是个验证码插件,如果每次生成的验证图片都保存下来,对服务器硬盘资源占用将是个大问题。

  难点四:如何保证验证信息的安全

  在我将后端代码全部开发完成,前端也封装好了一个 jQuery 插件后,发现了一个大问题,就是如果用户通过特殊手段跳过验证码验证,直接提交表单或者相关业务操作怎么办?

  因为验证码是以插件的形式存在,所以在调用的参数里有一个 callback 参数,用于验证成功后执行网站本身业务逻辑的代码。这样就可能会有个问题,我用 chrome 按 F12 打开开发者工具,直接在任务台里输入了提交表单的代码并回车执行,然后表单顺利提交了,完完全全跳过了验证。

  解决这个问题也不复杂,我思考了传统验证码的验证流程,核心一点就是它是随表单一起提交并做验证的,但由于我这个验证码的特殊性,所以只能增加一个后端二次验证,也就是前端初步验证后,将验证信息随表单提交到后端进行二次验证即可,同时,后端的二次验证成功后,将 session 清除,避免重复刷新提交表单造成能跳过二次验证的问题。


  以上就是我对这个项目的难点总结,如果你看到这了,希望对感兴趣的你有点启发,这个项目我同时放在的  和  上,,有兴趣的可以关注下。

  以下是针对前两个难点写的一个小demo,如果对完整的源码一时半会难理解的话,可以 copy 以下代码到本地,替换下字体和图片,然后运行一下看看效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
header(
"Content-type: text/html; charset=utf-8"
);
error_reporting
(E_ERROR | E_WARNING | E_PARSE);
 
$imagePath 
'bg.jpg'
;
$fontPath 
'msyh.ttc'
;
//为什么要乘0.75?因为 imagefttext 方法里的 size 参数使用磅(point)做为单位的,所以需要进行转换,转换为像素
$fontSize 
= 50 * 0.75;
 
//以“博客园”三个字举例,将文字、尺寸等信息存入数组
foreach
(
array
(
'博'
'客'
'园'
as 
$v
){
    
$fontarea  
= imagettfbbox(
$fontSize
, 0, 
$fontPath
$v
);
    
$textWidth 
$fontarea
[2] - 
$fontarea
[0];
    
$textHeight 
$fontarea
[1] - 
$fontarea
[7];
    
$tmp
[
'text'
] = 
$v
;
    
$tmp
[
'size'
] = 
$fontSize
;
    
$tmp
[
'width'
] = 
$textWidth
;
    
$tmp
[
'height'
] = 
$textHeight
;
    
$textArr
[] = 
$tmp
;
}
 
//获取背景底图宽高和类型信息
list(
$imageWidth
$imageHeight
$imageType
) = 
getimagesize
(
$imagePath
);
 
//随机生成汉字位置,并附加存入数组
foreach
(
$textArr 
as 
&
$v
){
    
list(
$x
$y
) = randPosition(
$textArr
$imageWidth
$imageHeight
$v
[
'width'
], 
$v
[
'height'
]);
    
$v
[
'x'
] = 
$x
;
    
$v
[
'y'
] = 
$y
;
}
unset(
$v
);
 
//创建图片的实例
$image 
= imagecreatefromstring(
file_get_contents
(
$imagePath
));
//字体颜色
$color 
= imagecolorallocate(
$image
, 0, 0, 0);
//绘画文字
foreach
(
$textArr 
as 
$v
){
    
imagefttext(
$image
$v
[
'size'
], 0, 
$v
[
'x'
], 
$v
[
'y'
], 
$color
$fontPath
$v
[
'text'
]);
}
//生成图片
switch
(
$imageType
){
    
case 
1:
//GIF
        
header(
'Content-Type: image/gif'
);
        
imagegif(
$image
);
        
break
;
    
case 
2:
//JPG
        
header(
'Content-Type: image/jpeg'
);
        
imagejpeg(
$image
);
        
break
;
    
case 
3:
//PNG
        
header(
'Content-Type: image/png'
);
        
imagepng(
$image
);
        
break
;
    
default
:
        
break
;
}
imagedestroy(
$image
);
 
//随机生成位置布局
function 
randPosition(
$textArr
$imgW
$imgH
$fontW
$fontH
){
    
$return 
array
();
    
$x 
= rand(0, 
$imgW 
$fontW
);
    
$y 
= rand(
$fontH
$imgH
);
    
if
(!checkPosition(
$textArr
$x
$y
$fontW
$fontH
)){
        
$return 
= randPosition(
$textArr
$imgW
$imgH
$fontW
$fontH
);
    
}
else
{
        
$return 
array
(
$x
$y
);
    
}
    
return 
$return
;
}
function 
checkPosition(
$textArr
$x
$y
$w
$h
){
    
$flag 
= true;
    
foreach
(
$textArr 
as 
$v
){
        
if
(isset(
$v
[
'x'
]) && isset(
$v
[
'y'
])){
            
//分别判断X和Y是否都有交集,如果都有交集,则判断为覆盖
            
$flagX 
= true;
            
if
(
$v
[
'x'
] > 
$x
){
                
if
(
$x 
$w 
$v
[
'x'
]){
                    
$flagX 
= false;
                
}
            
}
else 
if
(
$x 
$v
[
'x'
]){
                
if
(
$v
[
'x'
] + 
$v
[
'width'
] > 
$x
){
                    
$flagX 
= false;
                
}
            
}
else
{
                
$flagX 
= false;
            
}
            
$flagY 
= true;
            
if
(
$v
[
'y'
] > 
$y
){
                
if
(
$y 
$h 
$v
[
'y'
]){
                    
$flagY 
= false;
                
}
            
}
else 
if
(
$y 
$v
[
'y'
]){
                
if
(
$v
[
'y'
] + 
$v
[
'height'
] > 
$y
){
                    
$flagY 
= false;
                
}
            
}
else
{
                
$flagY 
= false;
            
}
            
if
(!
$flagX 
&& !
$flagY
){
                
$flag 
= false;
            
}
        
}
    
}
    
return 
$flag
;
}

  参考资料:

1、

2、

3、

60c18c1cgw1ec84xggwymj21dw0pcn63.jpg
HoorayOS - WEB桌面应用框架
这是一款备受好评的 Web 桌面应用框架,你可以用它二次开发出类似 Q+Web 这类的桌面应用网站,也可以开发出适用于各种项目的桌面管理系统。
官网:
    本文转自胡尐睿丶博客园博客,原文链接:http://www.cnblogs.com/hooray/p/5354482.html
,如需转载请自行联系原作者
你可能感兴趣的文章
由String类的Split方法所遇到的两个问题
查看>>
Python3.4 12306 2015年3月验证码识别
查看>>
从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
查看>>
自制操作系统Antz day11——实现shell(下)命令响应
查看>>
windows查看端口占用
查看>>
strongswan ikev2 server on ubuntu 14.04
查看>>
Yii用ajax实现无刷新检索更新CListView数据
查看>>
JDBC的事务
查看>>
linux服务器CPU参数/proc/cpuinfo
查看>>
haystack+Elasticsearch搜素引擎
查看>>
UEFI系统安装U盘的制作方式
查看>>
读《Oracle DBA工作笔记》知识点-获取创建语句
查看>>
Io流的概述
查看>>
js功能实现top轮播图
查看>>
App 卸载记录
查看>>
TCAM 与CAM
查看>>
POJ 3667 & HDU 3308 & HDU 3397 线段树的区间合并
查看>>
JQuery 的Ajax的使用
查看>>
jmeter设置json断言
查看>>
浅析C#代理
查看>>