feat 增加图形验证码

This commit is contained in:
damonyuan
2026-07-22 14:52:47 +08:00
parent 894c6f1048
commit dd43e8f2ab
5 changed files with 217 additions and 6 deletions

View File

@@ -23,12 +23,23 @@ namespace app\admin\controller;
use app\admin\logic\LoginLogic;
use app\admin\validate\Login;
use app\common\server\CaptchaService;
use Exception;
use think\facade\Url;
class Account extends AdminBase
{
public $like_not_need_login = ['login'];
public $like_not_need_login = ['login', 'captcha'];
/**
* @notes 图形验证码
* @throws Exception
*/
public function captcha()
{
$this->_success('OK', CaptchaService::create());
}
/**
* 登录
@@ -61,4 +72,4 @@ class Account extends AdminBase
$url = Url::build('account/login');
$this->redirect($url);
}
}
}

View File

@@ -19,6 +19,7 @@
namespace app\admin\validate;
use app\common\server\CaptchaService;
use think\Db;
use think\facade\Cache;
use think\Validate;
@@ -27,15 +28,38 @@ class Login extends Validate
{
protected $rule = [
'account' => 'require',
'captcha_key' => 'require',
'captcha' => 'require|checkCaptcha',
'password' => 'require|password',
];
protected $message = [
'account.require' => '账号不能为空',
'captcha_key.require' => '图形验证码已失效,请重新获取',
'captcha.require' => '请输入图形验证码',
'password.require' => '密码不能为空',
'password.password' => '账号密码错误',
];
/**
* @notes 校验图形验证码
* @param $captcha
* @param $rule
* @param $data
* @return string|true
*/
public function checkCaptcha($captcha, $rule, $data)
{
unset($rule);
$key = trim($data['captcha_key'] ?? '');
if (CaptchaService::verify($key, (string) $captcha)) {
return true;
}
return '图形验证码错误';
}
/**
* 账号密码验证码
* @param $password
@@ -90,4 +114,4 @@ class Login extends Validate
return true;
}
}
}

View File

@@ -21,12 +21,23 @@
namespace app\api\controller;
use app\api\logic\LoginLogic;
use app\api\validate\WechatLoginValidate;
use app\common\server\CaptchaService;
use app\common\server\ConfigServer;
use Exception;
class Account extends ApiBase
{
public $like_not_need_login = ['register','applogin', 'login', 'mnplogin', 'codeurl', 'oalogin', 'oplogin','logout','smslogin', 'uinAppLogin', 'silentLogin', 'authLogin'];
public $like_not_need_login = ['captcha', 'register','applogin', 'login', 'mnplogin', 'codeurl', 'oalogin', 'oplogin','logout','smslogin', 'uinAppLogin', 'silentLogin', 'authLogin'];
/**
* @notes 图形验证码
* @throws Exception
*/
public function captcha()
{
$this->_success('OK', CaptchaService::create());
}
/**
@@ -323,4 +334,4 @@ class Account extends ApiBase
$this->_success('绑定成功');
}
}
}

View File

@@ -27,6 +27,7 @@ use think\Db;
use think\facade\Cache;
use think\Validate;
use app\common\logic\SmsLogic;
use app\common\server\CaptchaService;
class Login extends Validate
{
@@ -39,6 +40,8 @@ class Login extends Validate
protected $rule = [
'account' => 'require',
'captcha_key' => 'require',
'captcha' => 'require|checkCaptcha',
'password' => 'require|password',
'client' => 'require|in:'. Client_::mnp . ',' . Client_::oa . ',' . Client_::ios . ',' . Client_::android. ',' .Client_::pc. ','. Client_::h5,
'code'=>'require|checkCode',
@@ -46,6 +49,8 @@ class Login extends Validate
protected $message = [
'account.require' => '请输入账号或手机号',
'captcha_key.require' => '图形验证码已失效,请重新获取',
'captcha.require' => '请输入图形验证码',
'password.require' => '请输入密码',
'password.password' => '密码错误',
'client.in' => '当前只支持h5和app登录',
@@ -62,6 +67,25 @@ class Login extends Validate
$this->only(['account','code','client']);
}
/**
* @notes 校验图形验证码
* @param $captcha
* @param $rule
* @param $data
* @return string|true
*/
public function checkCaptcha($captcha, $rule, $data)
{
unset($rule);
$key = trim($data['captcha_key'] ?? '');
if (CaptchaService::verify($key, (string) $captcha)) {
return true;
}
return '图形验证码错误';
}
public static function checkCode($value,$rule,$data){
$sms_logic = new SmsLogic($data['message_key'],$data['account'],$value);
@@ -145,4 +169,4 @@ class Login extends Validate
return true;
}
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace app\common\server;
use Exception;
use think\facade\Cache;
class CaptchaService
{
const CHARS = '0123456789';
const LENGTH = 4;
const EXPIRE_SECONDS = 60;
const CACHE_PREFIX = 'captcha:';
/**
* @notes 创建图形验证码
* @return array
* @throws Exception
*/
public static function create()
{
$code = self::createCode();
$key = md5(uniqid((string) random_int(100000, 999999), true));
Cache::set(self::CACHE_PREFIX . $key, $code, self::EXPIRE_SECONDS);
return [
'key' => $key,
'image' => 'data:image/png;base64,' . base64_encode(self::drawImage($code)),
'expire' => self::EXPIRE_SECONDS,
];
}
/**
* @notes 校验图形验证码,一次性消费
* @param string $key
* @param string $code
* @return bool
*/
public static function verify($key, $code)
{
$cacheKey = self::CACHE_PREFIX . trim((string) $key);
$captcha = Cache::get($cacheKey);
Cache::rm($cacheKey);
if (empty($captcha)) {
return false;
}
return hash_equals($captcha, strtolower(trim((string) $code)));
}
/**
* @notes 创建验证码字符
* @return string
* @throws Exception
*/
private static function createCode()
{
$code = '';
$maxIndex = strlen(self::CHARS) - 1;
for ($i = 0; $i < self::LENGTH; $i++) {
$code .= self::CHARS[random_int(0, $maxIndex)];
}
return $code;
}
/**
* @notes 绘制验证码图片
* @param string $code
* @return string
* @throws Exception
*/
private static function drawImage($code)
{
$width = 120;
$height = 42;
$image = imagecreatetruecolor($width, $height);
$backgroundColor = imagecolorallocate($image, 245, 247, 250);
imagefilledrectangle($image, 0, 0, $width, $height, $backgroundColor);
for ($i = 0; $i < 5; $i++) {
$lineColor = imagecolorallocate(
$image,
random_int(90, 180),
random_int(90, 180),
random_int(90, 180)
);
imageline(
$image,
random_int(0, $width),
random_int(0, $height),
random_int(0, $width),
random_int(0, $height),
$lineColor
);
}
for ($i = 0; $i < 120; $i++) {
$pixelColor = imagecolorallocate(
$image,
random_int(120, 220),
random_int(120, 220),
random_int(120, 220)
);
imagesetpixel(
$image,
random_int(0, $width - 1),
random_int(0, $height - 1),
$pixelColor
);
}
for ($i = 0; $i < self::LENGTH; $i++) {
$textColor = imagecolorallocate(
$image,
random_int(20, 90),
random_int(20, 90),
random_int(20, 90)
);
imagestring(
$image,
5,
18 + $i * 23,
random_int(10, 18),
$code[$i],
$textColor
);
}
ob_start();
imagepng($image);
$content = ob_get_clean();
imagedestroy($image);
return $content;
}
}