SARS 学习计划的第二周即将结束了。参加的数字识别和新生入学小助手小组的开发都在平稳的推进中,由于在两组中分别选择了微信小程序和前端开发。因此我主要还是以学习前端和小程序开发为主,暂时还没有具体的开发任务,只是进行了一些打杂工作。数字识别小组的任务是识别软微综合信息服务平台的验证码。没有真实的验证码作为训练数据,就不能完成这个任务。由于组里的各位大佬都忙于算实现,因此就由前端组我来生成这些验证码。

  首先通过观察服务器的响应头中的 X-Powered-By 字段可以发现服务端采用的是 ThinkPHP。随意构造一个链接发现未关闭 Debug 模式,使用的 ThinkPHP 版本为 3.2.2。
ThinkPHP3.2.2

图 1 原始图像

  知道了框架的版本问题就很简单了,去 ThinkPHP 官网下载框架并在电脑上部署。
ThinkPHP
图 2 部署 ThinkPHP

  通过阅读 PHP 语法和 ThinkPHP 的文档,尝试修改 IndexController.class.php 调用 ThinkPHP 提供的 Think\Verify 类。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
namespace Home\Controller;
use Think\Controller;
use Think\Verify;

class IndexController extends Controller
{
public function index()
{
$verify = new Verify($config);
$verify->entry();
}
}

  访问首页,成功显示验证码图像。
生成验证码图像

图 3 生成验证码图像

  修改验证码参数使得验证码风格与综合服务平台验证码一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class IndexController extends Controller
{
public function index()
{
$config = array(
'useImgBg' => false, // 使用背景图片
'fontSize' => 16, // 验证码字体大小(px)
'useCurve' => true, // 是否画混淆曲线
'useNoise' => false, // 是否添加杂点
'imageH' => 37, // 验证码图片高度
'imageW' => 120, // 验证码图片宽度
'length' => 4, // 验证码位数
'codeSet' => '0123456789', // 验证码字符集合
);
for($num = 0; $num < 10; $num++) {
$verify = new Verify($config);
$verify->entry();
}
}
}

修改参数后的验证码图像

图 4 修改参数后的验证码图像

  尽管实现了验证码生成,但此时并不知道生成的验证码的明文,而且验证码是展示在网页上的。为了实现一个能够批量生成验证码到本地的工具,尝试对 Think\Verify 进行修改。通过阅读代码,首先找到输出验证码的代码块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 保存验证码
$key = $this->authcode($this->seKey);
$code = $this->authcode(strtoupper(implode('', $code)));
$secode = array();
$secode['verify_code'] = $code; // 把校验码保存到session
$secode['verify_time'] = NOW_TIME; // 验证码创建时间
session($key . $id, $secode);

header('Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header("content-type: image/png");

// 输出图像
imagepng($this->_image);
imagedestroy($this->_image);

  阅读 PHP 文档了解 imagepng() 的使用方法,修改代码实现保存。

1
2
3
// 保存验证码
imagepng($this->_image, $code.'.png');
imagedestroy($this->_image);

  访问网页根目录下出现了一张验证码图像。
验证码保存到本地

图 5 验证码保存到本地

  尽管出现了一张验证码图像,但是文件名却不是我们想要的,继续阅读代码发现ThinkPHP为了安全会将生成好的验证码字符串加密后存储到session中,用户传来的验证码字符串也需要先进行加密,再与session中的字符串进行对比来判断是否输入正确。但是这些步骤以及服务器响应头对于生成训练数据来说并没有什么用,全部删除仅保留两行,并将每个字符拼接成字符串。

1
2
imagepng($this->_image, $code[0].$code[1].$code[2].$code[3].'.png');
imagedestroy($this->_image);

  访问网页,此时出现了一张正确命名的验证码图像。
验证码保存到本地

图 6 正确命名的验证码

  完成了这些工作后只需要增加一个循环就可以批量生成验证码图像了。尝试生成 10 个验证码图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public function index()
{
$config = array(
'useImgBg' => false, // 使用背景图片
'fontSize' => 16, // 验证码字体大小(px)
'useCurve' => true, // 是否画混淆曲线
'useNoise' => false, // 是否添加杂点
'imageH' => 37, // 验证码图片高度
'imageW' => 120, // 验证码图片宽度
'length' => 4, // 验证码位数
'codeSet' => '0123456789', // 验证码字符集合
);
for($num = 0; $num < 10; $num++) {
$verify = new Verify($config);
$verify->entry();
}
}

验证码保存到本地

图 7 生成的 10 张验证码图像

  尽管面对的是一门不了解的语言和框架,由于有充足的文档,因此生成工作还是比较简单的。