当前位置: 首页 > news >正文

thinkphp实现图像验证码

示例

在这里插入图片描述

服务类 app\common\lib\captcha

<?php
namespace app\common\lib\captcha;use think\facade\Cache;
use think\facade\Config;
use Exception;class Captcha
{private $im = null; // 验证码图片实例private $color = null; // 验证码字体颜色// 默认配置protected $config = ['length'      => 4,      // 验证码位数'fontSize'    => 25,     // 字体大小(px)'imageH'      => 0,      // 验证码高度'imageW'      => 0,      // 验证码宽度'useCurve'    => true,   // 是否画混淆曲线'useNoise'    => false,  // 是否添加杂点(已禁用)'bg'          => [243, 251, 254], // 背景颜色'fontttf'     => '',     // 字体文件路径'useZh'       => false,  // 使用中文验证码'math'        => false,  // 算术验证码'alpha'       => 0,      // 透明度(0-127)'api'         => false,  // API模式'fontPath'    => '',     // 字体文件目录'bgPath'      => '',     // 背景图片目录'expire'      => 1800,   // 验证码过期时间(s)];// 简化后的验证码字符集合(仅数字和大写字母)protected $codeSet = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';/*** 构造函数* @param array $config 配置参数*/public function __construct(array $config = []){// 合并配置参数$this->config = array_merge($this->config, Config::get('captcha', []), $config);// 设置字体路径if (empty($this->config['fontPath'])) {$this->config['fontPath'] = __DIR__ . '/ttfs/';}}/*** 生成验证码* @param string $uniqueId 前端传递的唯一标识(如时间戳)* @return string 图片二进制内容*/public function create(string $uniqueId = ''): string{// 清理过期缓存$this->clearExpiredCaptchas();// 如果未提供 uniqueId,则生成一个默认值if (empty($uniqueId)) {$uniqueId = uniqid('captcha_');}// 生成验证码文本$generator = $this->generate($uniqueId);// 计算图片宽高$this->config['imageW'] = $this->config['imageW'] ?: $this->config['length'] * $this->config['fontSize'] * 1.5;$this->config['imageH'] = $this->config['imageH'] ?: $this->config['fontSize'] * 2;// 创建图片资源$this->im = imagecreate((int)$this->config['imageW'], (int)$this->config['imageH']);// 设置背景色$bgColor = imagecolorallocate($this->im,$this->config['bg'][0],$this->config['bg'][1],$this->config['bg'][2]);imagefill($this->im, 0, 0, $bgColor);// 设置字体颜色$this->color = imagecolorallocate($this->im, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));// 添加干扰线if ($this->config['useCurve']) {$this->writeCurve();}// 绘制验证码$text = str_split($generator['value']);$space = $this->config['imageW'] / $this->config['length'];foreach ($text as $index => $char) {// 计算位置$x = $space * $index + mt_rand(5, 10);$y = $this->config['imageH'] / 2 + $this->config['fontSize'] / 2;$angle = mt_rand(-15, 15);imagettftext($this->im,(int)$this->config['fontSize'],$angle,(int)$x,(int)$y,$this->color,$this->getFont(),$char);}ob_start();imagepng($this->im);$content = ob_get_clean();imagedestroy($this->im);return $content;}/*** 验证验证码* @param string $code 用户输入的验证码* @param string $uniqueId 前端传递的唯一标识(如时间戳)* @return bool*/public function check(string $code, string $uniqueId = ''): bool{if (empty($uniqueId)) {return false;}// 从 Cache 中获取数据$cacheData = Cache::get($uniqueId);if (!$cacheData || time() - $cacheData['time'] > $this->config['expire']) {$this->removeCaptchaFromRecords($uniqueId);return false;}// 验证码校验$result = password_verify(strtoupper($code), $cacheData['key']);return $result;}/*** 生成验证码文本* @param string $uniqueId 前端传递的唯一标识(如时间戳)* @return array ['value' => 显示的文本, 'key' => 加密后的验证码]*/protected function generate(string $uniqueId): array{$bag = '';$characters = str_split($this->codeSet);for ($i = 0; $i < $this->config['length']; $i++) {$bag .= $characters[random_int(0, count($characters) - 1)];}$key = strtoupper($bag);// 使用 Bcrypt 加密验证码$hash = password_hash($key, PASSWORD_BCRYPT, ['cost' => 10]);// 将验证码信息存储到 Cache 中Cache::set($uniqueId, ['key' => $hash,'time' => time(),// 'raw' => $key // 调试用,正式环境移除], $this->config['expire']);// 记录到清理队列$this->addCaptchaToRecords($uniqueId);return ['value' => $bag, 'key' => $hash];}/*** 添加验证码记录到清理队列*/protected function addCaptchaToRecords(string $uniqueId): void{$records = Cache::get('captcha_records', []);$records[$uniqueId] = time() + $this->config['expire'];// 限制最大记录数,防止内存占用过大if (count($records) > 1000) {$records = array_slice($records, -500, null, true);}Cache::set('captcha_records', $records);}/*** 从清理队列中移除验证码记录*/protected function removeCaptchaFromRecords(string $uniqueId): void{$records = Cache::get('captcha_records', []);unset($records[$uniqueId]);Cache::set('captcha_records', $records);}/*** 清理过期的验证码缓存*/protected function clearExpiredCaptchas(): void{// 每小时清理一次$lastClear = Cache::get('last_captcha_clear', 0);if (time() - $lastClear < 3600) {return;}$records = Cache::get('captcha_records', []);$now = time();$cleaned = false;foreach ($records as $uid => $expireTime) {if ($expireTime < $now) {Cache::delete($uid);unset($records[$uid]);$cleaned = true;}}if ($cleaned) {Cache::set('captcha_records', $records);Cache::set('last_captcha_clear', time());}}/*** 获取字体文件路径* @return string* @throws Exception*/protected function getFont(): string{if (!empty($this->config['fontttf'])) {return $this->config['fontttf'];}$fonts = glob($this->config['fontPath'] . '*.ttf') +glob($this->config['fontPath'] . '*.otf');if (empty($fonts)) {throw new Exception('验证码字体文件不存在,请检查字体路径: ' . $this->config['fontPath']);}return $fonts[array_rand($fonts)];}/*** 画干扰曲线*/protected function writeCurve(): void{$px = $py = 0;// 曲线前部分$A = mt_rand(1, (int)($this->config['imageH'] / 2)); // 振幅$b = mt_rand((int)(-$this->config['imageH'] / 4), (int)($this->config['imageH'] / 4)); // Y轴偏移$f = mt_rand((int)(-$this->config['imageH'] / 4), (int)($this->config['imageH'] / 4)); // X轴偏移$T = mt_rand($this->config['imageH'], $this->config['imageW'] * 2); // 周期$w = (2 * M_PI) / $T;$px1 = 0; // 起始X坐标$px2 = mt_rand((int)($this->config['imageW'] / 2), (int)($this->config['imageW'] * 0.8)); // 结束X坐标for ($px = $px1; $px <= $px2; $px++) {if ($w != 0) {$py = $A * sin($w * $px + $f) + $b + $this->config['imageH'] / 2;$i = (int)($this->config['fontSize'] / 5);while ($i > 0) {imagesetpixel($this->im, $px + $i, $py + $i, $this->color);$i--;}}}// 曲线后部分$A = mt_rand(1, (int)($this->config['imageH'] / 2));$f = mt_rand((int)(-$this->config['imageH'] / 4), (int)($this->config['imageH'] / 4));$T = mt_rand($this->config['imageH'], $this->config['imageW'] * 2);$w = (2 * M_PI) / $T;$b = $py - $A * sin($w * $px + $f) - $this->config['imageH'] / 2;$px1 = $px2;$px2 = $this->config['imageW'];for ($px = $px1; $px <= $px2; $px++) {if ($w != 0) {$py = $A * sin($w * $px + $f) + $b + $this->config['imageH'] / 2;$i = (int)($this->config['fontSize'] / 5);while ($i > 0) {imagesetpixel($this->im, $px + $i, $py + $i, $this->color);$i--;}}}}
}

控制器调用

// 生成图形验证码public function getCaptcha(){$uid = 'captcha_' . uniqid('', true);$captcha = new \app\common\lib\captcha\Captcha();$img = $captcha->create($uid);return json(['image' => 'data:image/png;base64,'.base64_encode($img),'uid' => $uid]);}// 校验图形验证码public function checkCaptcha(){$code = input('post.code');$uid = input('post.uid');$captcha = new \app\common\lib\captcha\Captcha();$result = $captcha->check($code, $uid);return json(['success' => $result,'input_code' => $code,'uid' => $uid]);}
http://www.xdnf.cn/news/19927.html

相关文章:

  • 2025年03月中国电子学会青少年软件编程(Python)等级考试试卷(一级)真题
  • DDS Discovery数据
  • PM2模块
  • AI专题(一)----NLP2SQL探索以及解决方案
  • std::unordered_set(C++)
  • Java课程内容大纲(附重点与考试方向)
  • 算法01-最小生成树prim算法
  • C语言复习笔记--字符函数和字符串函数(上)
  • Xen安装ubuntu并启动过程记录
  • final关键字带来的问题
  • 大数据赋能,全面提升‘企业服务平台’实际效能!
  • 见多识广3:帕累托最优解与帕累托前沿
  • HAL详解
  • C#学习第16天:聊聊反射
  • API 即 MCP|Higress 发布 MCP Marketplace,加速存量 API 跨入 MCP 时代
  • 电脑开机启动慢的原因
  • Python 的 pip 命令详解,涵盖常用操作和高级用法
  • ES数据库索引报错
  • 十、数据库day02--SQL语句01
  • 基于Python的MCP Server技术解析:从AI代理到实时数据处理的智能化实践
  • 博客系统案例练习-回顾
  • MMAction2安装
  • 3、整合前端基础交互页面
  • 幽灵依赖与常见依赖管理
  • C++每日训练 Day 17:构建响应式加载动画与异步数据处理
  • 笔记本电脑屏幕闪烁是怎么回事 原因及解决方法
  • 【Drools+springboot3规则匹配】
  • 【计算机网络 | 第一篇】计算机网络基础知识
  • 【Linux】部署vfstpd服务端,让客户端通过访问不同的端口号,可以实现访问不同的目录
  • 刀片服务器的散热构造方式