Files
2026-08-08 18:27:38 +08:00

78 lines
2.6 KiB
PHP

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
/**
* 图形验证码(轻量、零依赖,基于 GD 绘制)。
* - GET /captcha 生成一张 4 位字符的图片,并把答案写入 session('captcha')。
* - 登录接口校验用户输入与 session 中的值(大小写不敏感),校验后清空,防止复用。
* - 仅用于「人/机」区分,配合登录限流构成基础安全管控。
*/
class CaptchaController extends Controller
{
public function show(Request $request)
{
$code = $this->generateCode(4);
Session::put('captcha', $code);
$width = 120;
$height = 44;
$img = imagecreatetruecolor($width, $height);
// 背景
$bg = imagecolorallocate($img, 244, 247, 250);
imagefilledrectangle($img, 0, 0, $width, $height, $bg);
// 干扰线
for ($i = 0; $i < 6; $i++) {
$c = imagecolorallocate($img, rand(160, 210), rand(160, 210), rand(160, 210));
imageline($img, rand(0, $width), rand(0, $height), rand(0, $width), rand(0, $height), $c);
}
// 字符(随机色 + 轻微纵向抖动)
$palette = [[74, 138, 244], [110, 140, 176], [43, 182, 164], [123, 126, 240], [200, 120, 60]];
$len = strlen($code);
$step = (int) ($width / ($len + 1));
for ($i = 0; $i < $len; $i++) {
$col = $palette[array_rand($palette)];
$c = imagecolorallocate($img, $col[0], $col[1], $col[2]);
$x = $step * ($i + 1) - 10 + rand(-3, 3);
$y = rand(8, 18);
imagestring($img, 5, $x, $y, $code[$i], $c);
}
// 噪点
for ($i = 0; $i < 50; $i++) {
$c = imagecolorallocate($img, rand(180, 225), rand(180, 225), rand(180, 225));
imagesetpixel($img, rand(0, $width), rand(0, $height), $c);
}
ob_start();
imagepng($img);
$data = ob_get_clean();
imagedestroy($img);
return response($data, 200)
->header('Content-Type', 'image/png')
->header('Cache-Control', 'no-store, no-cache, must-revalidate, private')
->header('Pragma', 'no-cache');
}
/**
* 生成验证码字符(去除易混淆的 0/O/1/I/L)。
*/
protected function generateCode(int $len): string
{
$pool = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$out = '';
for ($i = 0; $i < $len; $i++) {
$out .= $pool[random_int(0, strlen($pool) - 1)];
}
return $out;
}
}