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

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]);}

相关文章:

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 $co…...

【k8s系列4】工具介绍

1、虚拟机软件 vmware workstation 2、shell 软件 MobaXterm 3、centos7.9 下载地址 &#xff08;https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/?spma2c6h.25603864.0.0.374bf5adOaiFPW&#xff09; 4、上网软件...

微博辐射源和干扰机

微波辐射源和干扰机是电子战和通信领域中的两个重要概念&#xff0c;它们在军事、民用及科研中具有广泛应用。以下是两者的详细解析及其相互关系&#xff1a; ‌1. 微波辐射源‌ ‌定义‌&#xff1a; 微波辐射源是指能够主动发射微波&#xff08;频率范围通常为 ‌300 MHz&…...

计算机网络——网络模型

一、OSI七层模型 &#xff08;1&#xff09;客户端发送请求时 OSI 七层模型的运作流程 应用层&#xff08;Application Layer&#xff09; 用户通过浏览器输入URL&#xff08;如https://example.com&#xff09;&#xff0c;根据协议类型&#xff08;HTTP/HTTPS&#xff09;确…...

Spark-SQL核心编程2

路径问题 相对路径与绝对路径&#xff1a;建议使用绝对路径&#xff0c;避免复制粘贴导致的错误&#xff0c;必要时将斜杠改为双反斜杠。 数据处理与展示 SQL 风格语法&#xff1a;创建临时视图并使用 SQL 风格语法查询数据。 DSL 风格语法&#xff1a;使用 DSL 风格语法查询…...

Java 序列化与反序列化终极解析

Java 序列化与反序列化终极解析 1. 核心概念 (1) 什么是序列化&#xff1f; 定义&#xff1a;将对象转换为字节流的过程&#xff08;对象 → 字节&#xff09; 目的&#xff1a; 持久化存储&#xff08;如保存到文件&#xff09; 网络传输&#xff08;如RPC调用&#xff09…...

STM32单片机入门学习——第41节: [12-1] Unix时间戳

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难&#xff0c;但我还是想去做&#xff01; 本文写于&#xff1a;2025.04.18 STM32开发板学习——第41节: [12-1] Unix时间戳 前言开发板说明引用解答和科普一…...

无人机自主导航与路径规划技术要点!

一、自主导航与路径规划技术要点 1. 传感器融合 GPS/北斗定位&#xff1a;提供全局定位&#xff0c;但在室内或遮挡环境下易失效。 惯性测量单元&#xff08;IMU&#xff09;**&#xff1a;通过加速度计和陀螺仪实时追踪姿态&#xff0c;弥补GPS信号丢失时的定位空缺。 …...

AI绘画SD中,如何保持生成人物角色脸部一致?Stable Diffusion精准控制AI人像一致性两种实用方法教程!

在AI绘画StableDiffusion中&#xff0c;一直都有一个比较困难的问题&#xff0c;就是如何保证每次出图都是同一个人。今天就这个问题分享一些个人实践&#xff0c;大家和我一起来看看吧。 一. 有哪些实现方式 方式1&#xff1a;固定Seed种子值。 固定Seed种子值出来的图片人…...

java 设计模式 策略模式

简介 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为设计模式&#xff0c;旨在定义一系列算法&#xff0c;并将每一个算法封装起来&#xff0c;使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。换句话说&#xff0c;策略模式通过将不同的算法…...

std::set (C++)

std::set 1. 概述定义特点 2. 内部实现3. 性能特征4. 常用 API5. 使用示例6. 自定义比较器7. 注意事项与优化8. 使用建议 1. 概述 定义 template<class Key,class Compare std::less<Key>,class Allocator std::allocator<Key> > class std::set;特点 有…...

STM32 HAL 通用定时器延时函数

使用通用定时器TIM3&#xff0c;实现ms、us延时。 delay.c #include "delay.h" #include "stm32f1xx_hal.h"TIM_HandleTypeDef htim3;/*** brief 初始化定时器3用于延时* param 无* retval 无*/ void Delay_Init(void) {TIM_ClockConfigTypeDef sClock…...

RK3588S开发板将SPI1接口改成GPIO

参考官方教程&#xff1a;ROC-RK3588S-PC 一.基本知识&#xff1a; 1.GPIO引脚计算&#xff1a; ROC-RK3588S-PC 有 5 组 GPIO bank&#xff1a;GPIO0~GPIO4&#xff0c;每组又以 A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分&#xff0c;常用以下公式计算引脚&#xff1a;GPIO…...

PLOS ONE:VR 游戏扫描揭示了 ADHD 儿童独特的大脑活动

在孩子的成长过程中&#xff0c;总有那么一些“与众不同”的孩子。他们似乎总是坐不住&#xff0c;课堂上小动作不断&#xff0c;注意力难以集中&#xff0c;作业总是拖拖拉拉……这些行为常常被家长和老师简单地归结为“淘气”“不听话”。然而&#xff0c;他们可能并不只是“…...

DemoGen:用于数据高效视觉运动策略学习的合成演示生成

25年2月来自清华、上海姚期智研究院和上海AI实验室的论文“DemoGen: Synthetic Demonstration Generation for Data-Efficient Visuomotor Policy Learning”。 视觉运动策略在机器人操控中展现出巨大潜力&#xff0c;但通常需要大量人工采集的数据才能有效执行。驱动高数据需…...

极狐GitLab 账号限制有哪些?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 账户和限制设置 (BASIC SELF) 默认项目限制 您可以配置新用户能在其个人命名空间中创建的默认最大项目数。此限制仅影响更改…...

@JsonView + 单一 DTO:如何实现多场景 JSON 字段动态渲染

JsonView 单一 DTO&#xff1a;如何实现多场景 JSON 字段动态渲染 JsonView 单一 DTO&#xff1a;如何实现多场景 JSON 字段动态渲染1、JsonView 注解产生的背景2、为了满足不同场景下返回对应的属性的做法有哪些&#xff1f;2.1 最快速的实现则是针对不同场景新建不同的 DTO…...

JVM之经典垃圾回收器

一、垃圾回收算法 1. 标记-清除&#xff08;Mark-Sweep&#xff09; 步骤&#xff1a; 标记&#xff1a;遍历对象图&#xff0c;标记所有存活对象。清除&#xff1a;回收未被标记的垃圾对象。 特点&#xff1a;简单&#xff0c;但会产生内存碎片。 2. 标记-复制&#xff08;…...

15 nginx 中默认的 proxy_buffering 导致基于 http 的流式响应存在 buffer, 以 4kb 一批次返回

前言 这也是最近碰到的一个问题 直连 流式 http 服务, 发现 流式响应正常, 0.1 秒接收到一个响应 但是 经过 nginx 代理一层之后, 就发现了 类似于缓冲的效果, 1秒接收到 10个响应 最终 调试 发现是 nginx 的 proxy_buffering 配置引起的 然后 更新 proxy_buffering 为…...

人工智能学习框架完全指南(2025年更新版)

一、核心框架分类与适用场景 人工智能框架根据功能可分为深度学习框架、机器学习框架、强化学习框架和传统工具库,以下是主流工具及选型建议: 1. 深度学习框架 (1)PyTorch 核心优势:动态计算图、灵活性强,适合科研与快速原型开发,支持多模态任务(如NLP、CV) 。技术生…...

安卓手机万能遥控器APP推荐

软件介绍 安卓手机也能当“家电总控台”&#xff1f;这款小米旗下的万能遥控器APP&#xff0c;直接把遥控器做成“傻瓜式操作”——不用配对&#xff0c;不连蓝牙&#xff0c;点开就能操控电视、空调、机顶盒&#xff0c;甚至其他品牌的电器&#xff01;雷总这波操作直接封神&…...

颚式破碎机的设计

一、引言 颚式破碎机作为矿山、建材等行业的重要破碎设备&#xff0c;其性能优劣直接影响物料破碎效率与质量。随着工业生产规模的扩大和对破碎效率要求的提高&#xff0c;设计一款高效、稳定、节能的颚式破碎机具有重要意义。 二、设计需求分析 处理能力&#xff1a;根据目…...

PH热榜 | 2025-04-18

1. Wiza Monitor 标语&#xff1a;跟踪工作变动&#xff0c;接收Slack和电子邮件的提醒。 介绍&#xff1a;Wiza Monitor是一款用于追踪职位变动的工具&#xff0c;可以实时跟踪客户和潜在客户的工作变动&#xff0c;还可以通过电子邮件和Slack发送提醒&#xff0c;让你的客户…...

Android平台 Hal AIDL 系列文章目录

目录 1. Android Hal AIDL 简介2. AIDL 语言简介3. Android 接口定义语言 (AIDL)4. 定义AIDL 接口5. AIDL 中如何传递 Parcelable 对象6. 如何使用AIDL 定义的远程接口进行跨进程通信7. 适用于 HAL 的 AIDL8. Android Hal AIDL 编译调试9. 高版本Android (AIDL HAL) 沿用HIDL方…...

十、数据库day02--SQL语句01

文章目录 一、新建查询1.查询窗口的开启方法2. 单语句运行方法 二、数据库操作1.创建数据库2. 使用数据库3. 修改数据库4. 删除数据库和查看所有数据库5. 重点&#xff1a;数据库备份5.1 应用场景5.2 利用工具备份备份操作还原操作 5.3 扩展&#xff1a;使用命令备份 三、数据表…...

基于Atlas 800I A2 + Ubuntu 22.04 LTS 离线部署神州鲲泰问学一体机平台

一.环境信息 1.1.硬件信息 Atlas 800I A2 1.2.操作系统 版本&#xff1a; cat /etc/os-release PRETTY_NAME"Ubuntu 22.04 LTS" NAME"Ubuntu" VERSION_ID"22.04" VERSION"22.04 (Jammy Jellyfish)" VERSION_CODENAMEjammy IDubun…...

Shell脚本-变量是什么

在Shell脚本编程中&#xff0c;变量是一个非常基础且重要的概念。它们用于存储数据&#xff0c;并可以在整个脚本中引用这些数据来执行各种操作。理解如何定义、使用和管理变量是编写有效Shell脚本的关键。本文将详细介绍Shell脚本中的变量&#xff0c;包括其基本概念、类型以及…...

MCP 协议:AI 世界的 “USB-C 接口”,开启智能交互新时代

MCP协议&#xff1a;AI世界的“USB-C接口”&#xff0c;开启智能交互新时代 在AI技术飞速发展的今天&#xff0c;不同AI模型、应用与设备之间的交互和协同需求愈发迫切。就像USB-C接口统一了电子设备的数据传输与充电标准一样&#xff0c;**MCP协议&#xff08;Model Communic…...

服务器的算力已经被被人占用了,我如何能“无缝衔接”?

今天遇到一个问题&#xff0c;服务器已经被别人占用了&#xff0c;我又不知道什么时候他能结束&#xff0c;因此很难去训练自己的模型&#xff0c;隔一会去看看别人是否结束又太麻烦&#xff0c;于是便可以写这个脚本文件来自动检测服务器是否空闲&#xff0c;一有空闲就可以自…...

大数据面试问答-批处理性能优化

1. 数据存储角度 1.1 存储优化 列式存储格式&#xff1a;使用Parquet/ORC代替CSV/JSON&#xff0c;减少I/O并提升压缩率。 df.write.parquet("hdfs://path/output.parquet")列式存储减少I/O的核心机制&#xff1a; 列裁剪&#xff08;Column Pruning&#xff09; …...