# Image-validation
**Repository Path**: changrui_com/image-validation
## Basic Information
- **Project Name**: Image-validation
- **Description**: 发送验证码之前,让用户把一张被旋转过的图片扶正。这个包是 Composer SDK:出图、校验角度、签发一次性通行证。HTTP 接口和短信发送写在业务项目里。
- **Primary Language**: Unknown
- **License**: GPL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-23
- **Last Updated**: 2026-09-24
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 旋转扶正验证
发送验证码之前,让用户把一张被旋转过的图片扶正。这个包是 Composer SDK:出图、校验角度、签发一次性通行证。HTTP 接口和短信发送写在业务项目里。
图片字段是完整的 data URI,可以直接作为 `img` / `image` 的 `src`:
```text
data:image/png;base64,iVBORw0KGgo...
```
## 安装
```bash
composer require image-validation/rotate-captcha
```
需要 PHP 8.1+、`ext-gd`、`ext-json`。Redis 使用本机已安装的 `phpredis` 或 `predis`,在配置里用 `redis.client` 选择。
默认原图目录是包内 `captcha/in/image`,里面只放 jpg、png、webp,并且保持正放。
## 配置
Hyperf:
```bash
php bin/hyperf.php vendor:publish image-validation/rotate-captcha
```
生成 `config/autoload/rotate_captcha.php`。
Webman:
```bash
mkdir -p config/plugin/image-validation/rotate-captcha
cp vendor/image-validation/rotate-captcha/publish/rotate_captcha.php config/plugin/image-validation/rotate-captcha/rotate_captcha.php
```
PHP-FPM:
```bash
mkdir -p config
cp vendor/image-validation/rotate-captcha/publish/rotate_captcha.php config/rotate_captcha.php
```
FPM 使用的 `php.ini` 要启用 `gd`。`redis.client` 为 `phpredis` 时还要启用 `redis` 扩展。可以用 FPM 对应的 `php -m` 确认,它和命令行 PHP 不一定是同一份配置。
业务侧要改的主要是这三项:
```php
'redis' => [
'client' => 'phpredis', // phpredis 或 predis
'host' => '127.0.0.1',
'port' => 6379,
'username' => '',
'password' => '',
'database' => 0,
'timeout' => 2.0,
'prefix' => 'rotate_captcha:',
],
// 出图时顺时针旋转的度数,每次在闭区间内随机取一个角度
'rotate_min' => 30,
'rotate_max' => 330,
// 扶正后与正确角度相差在这个度数以内算通过
'verify_degrees' => 10,
```
`rotate_min` 与 `rotate_max` 写成同一个数时,每次都转这个固定度数。`image_dir` 为 `null` 时使用包内 `captcha/in/image`。
## 调用
```php
use ImageValidation\RotateCaptcha\Exception\PassException;
use ImageValidation\RotateCaptcha\Exception\VerifyException;
$result = $captcha->create('sms', $phone);
// $result['token']
// $result['image']
try {
$passed = $captcha->verify($token, (float) $angle);
// $passed['pass_token']
// $passed['expires_in']
} catch (VerifyException $e) {
// 未通过。$e->remaining() 为剩余次数,0 表示需要重新 create
}
try {
$captcha->assertPass($passToken, 'sms', $phone);
// 通过后再发送验证码
} catch (PassException $e) {
// 通行证无效、已使用,或与场景、手机号不一致
}
```
用户提交的 `angle` 是为了扶正而顺时针转过的角度。同一张图默认最多校验 3 次。失败结果里只有剩余次数,没有角度差。通行证默认 60 秒,使用一次后失效,并且绑定 `scene` 和手机号或邮箱。
### Hyperf
发布配置后,容器里可以直接注入 `ImageValidation\RotateCaptcha\RotateCaptcha`。
### Webman
```php
use ImageValidation\RotateCaptcha\Webman\RotateCaptchaProvider;
$captcha = RotateCaptchaProvider::make();
```
`make()` 读取 `config/plugin/image-validation/rotate-captcha/rotate_captcha.php`。也可以传入配置数组。
### PHP-FPM
在站点入口引入 Composer 自动加载,用配置文件创建实例。每个请求新建一次即可,不需要常驻进程。
```php
create((string) $payload['scene'], (string) $payload['target']));
return;
}
if ($action === 'verify') {
try {
echo json_encode($captcha->verify((string) $payload['token'], (float) $payload['angle']));
} catch (VerifyException $e) {
http_response_code(422);
echo json_encode(['message' => $e->getMessage(), 'remaining' => $e->remaining()]);
}
return;
}
if ($action === 'send') {
try {
$captcha->assertPass((string) $payload['pass_token'], 'sms', (string) $payload['phone']);
// 通过后再调用原来的短信发送
echo json_encode(['ok' => true]);
} catch (PassException $e) {
http_response_code(422);
echo json_encode(['message' => $e->getMessage()]);
}
}
```
Nginx 把该文件交给 `php-fpm` 即可,例如 `/captcha.php?action=create`。路由仍由业务自己定,SDK 不注册接口。
## 前端示例
滑块松手再提交角度。拖动过程中只用 CSS 旋转预览。
配合前端 [front-image-validation/rotate-captcha](https://gitee.com/changrui_com/front-image-validation) 使用的前端组件。弹窗里拖动左侧滑块,把圆形图片转正。松手后才提交角度。手机和 PC 都能拖。
组件不发请求地址,由业务传入 `create` 和 `verify`。验证通过后抛出 `passToken`,页面再去发验证码。
## 安装
```bash
npm install front-image-validation
```
Vue 3 项目需要已安装 `vue@3`。
## Vue 3
```vue
意见反馈
```
`create` 返回 `{ token, image }`,`image` 是后端给出的 `data:image/png;base64,...`。`verify` 成功返回 `{ pass_token, expires_in }`。失败时抛出带 `remaining` 的错误。剩余次数为 `0`,或消息里包含「失效」时,组件会自动重新拉图。
## uni-app
使用 Vue 3。页面里:
```vue
```
小程序里用手指拖滑块。H5 上鼠标和触摸都可以。意见反馈用同名插槽 `feedback`,不传就不显示。
## 属性
| 属性 | 说明 |
| --- | --- |
| `open` / `v-model:open` | 是否显示弹窗 |
| `scene` | 场景,例如 `sms` |
| `target` | 手机号或邮箱 |
| `create` | `(scene, target) => Promise<{ token, image }>` |
| `verify` | `(token, angle) => Promise<{ pass_token, expires_in }>` |
| `title` | 默认「安全验证」 |
| `subtitle` | 默认「请完成下方验证后继续操作」 |
| `hint` | 默认「拖动左侧滑块使图片为正」 |
滑块最左是 0°,最右是 360°,提交的是顺时针角度。点遮罩不会关闭,点右上角关闭。