PHP图形验证码技术实现
PHP图形验证码与验证码技术实现
验证码是防止自动化攻击的常用手段。从简单的数字验证码到行为验证,PHP都能实现。今天说说各种验证码的实现方式。
用GD库生成图片验证码是最传统的方式。核心思路是生成随机字符,画到图片上,添加干扰线和噪点来增加识别难度。
```php
class CaptchaGenerator
{
private int $width;
private int $height;
private int $length;
public function __construct(int $width = 150, int $height = 50, int $length = 4)
{
$this->width = $width;
$this->height = $height;
$this->length = $length;
}
public function generate(): array
{
$image = imagecreatetruecolor($this->width, $this->height);
// 背景色
$bgColor = imagecolorallocate($image, 255, 255, 255);
imagefill($image, 0, 0, $bgColor);
// 干扰线
for ($i = 0; $i < 5; $i++) {
$color = imagecolorallocate($image, rand(150, 200), rand(150, 200), rand(150, 200));
imageline($image, rand(0, $this->width), rand(0, $this->height), rand(0, $this->width), rand(0, $this->height), $color);
}
// 干扰点
for ($i = 0; $i < 100; $i++) {
$color = imagecolorallocate($image, rand(100, 200), rand(100, 200), rand(100, 200));
imagesetpixel($image, rand(0, $this->width), rand(0, $this->height), $color);
}
// 验证码字符
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$code = '';
for ($i = 0; $i < $this->length; $i++) {
$char = $chars[rand(0, strlen($chars) - 1)];
$code .= $char;
$textColor = imagecolorallocate($image, rand(0, 80), rand(0, 80), rand(0, 80));
$x = 20 + $i * 30 + rand(-3, 3);
$y = rand(15, 35);
$angle = rand(-20, 20);
imagestring($image, 5, $x, $y, $char, $textColor);
}
// 输出图片到内存
ob_start();
imagepng($image);
$imageData = ob_get_clean();
imagedestroy($image);
return [
'code' => $code,
'image' => base64_encode($imageData),
'mime' => 'image/png',
];
}
}
$generator = new CaptchaGenerator();
$captcha = $generator->generate();
echo "验证码: {$captcha['code']}\n";
echo "
\n";
?>
```
数学验证码比字符验证码更友好,用户只需要计算结果:
```php
class MathCaptcha
{
public function generate(): array
{
$a = rand(1, 50);
$b = rand(1, 30);
$operators = ['+', '-'];
$op = $operators[array_rand($operators)];
$expression = "$a $op $b";
$result = $op === '+' ? $a + $b : $a - $b;
$image = imagecreatetruecolor(150, 50);
$bgColor = imagecolorallocate($image, 245, 245, 245);
imagefill($image, 0, 0, $bgColor);
for ($i = 0; $i < 3; $i++) {
$lineColor = imagecolorallocate($image, rand(180, 220), rand(180, 220), rand(180, 220));
imageline($image, rand(0, 150), rand(0, 50), rand(0, 150), rand(0, 50), $lineColor);
}
$textColor = imagecolorallocate($image, 50, 50, 50);
$font = 5;
$textX = 30;
$textY = 18;
// 逐字符绘制
for ($i = 0; $i < strlen($expression); $i++) {
$charColors = [imagecolorallocate($image, rand(30, 100), rand(30, 100), rand(30, 100))];
imagestring($image, $font, $textX + $i * 20, $textY + rand(-3, 3), $expression[$i], $charColors[0]);
}
ob_start();
imagepng($image);
$imageData = ob_get_clean();
imagedestroy($image);
return [
'expression' => $expression,
'result' => $result,
'image' => base64_encode($imageData),
];
}
}
$math = new MathCaptcha();
$captcha = $math->generate();
echo "计算: {$captcha['expression']} = ?\n";
echo "结果: {$captcha['result']}\n";
?>
```
验证码的验证逻辑也很重要,需要防止重复使用和超时。
```php
class CaptchaValidator
{
private Redis $redis;
private int $ttl;
public function __construct(Redis $redis, int $ttl = 300)
{
$this->redis = $redis;
$this->ttl = $ttl;
}
public function store(string $key, string $code): void
{
$this->redis->setex("captcha:$key", $this->ttl, $code);
}
public function validate(string $key, string $input): bool
{
$stored = $this->redis->get("captcha:$key");
if ($stored === false) {
return false; // 过期或不存在
}
// 验证后立即删除,防止重复使用
$this->redis->del("captcha:$key");
return strtoupper($stored) === strtoupper($input);
}
public function delete(string $key): void
{
$this->redis->del("captcha:$key");
}
}
?>
```
验证码是用户体验和安全性的平衡。太复杂了用户看不清,太简单了又防不住自动化攻击。实际项目中还可以考虑Google reCAPTCHA、滑动验证、点选验证等方式,这些通常比纯图形验证码的用户体验更好,安全性也更高。
