-
-
@@ -49,7 +49,6 @@
{% block include_js %}
- {{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
{{ js_include('home/js/captcha.verify.phone.js') }}
{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Home/Views/verify/captcha.volt b/app/Http/Home/Views/verify/captcha.volt
new file mode 100644
index 00000000..171e875e
--- /dev/null
+++ b/app/Http/Home/Views/verify/captcha.volt
@@ -0,0 +1,105 @@
+{% extends 'templates/layer.volt' %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block include_js %}
+
+
+
+{% endblock %}
diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php
index 1e291fc1..5a3db91c 100644
--- a/app/Library/AppInfo.php
+++ b/app/Library/AppInfo.php
@@ -16,7 +16,7 @@ class AppInfo
protected $link = 'https://www.koogua.com';
- protected $version = '1.6.4';
+ protected $version = '1.6.5';
public function __get($name)
{
diff --git a/app/Library/Captcha.php b/app/Library/Captcha.php
new file mode 100644
index 00000000..ad366cbe
--- /dev/null
+++ b/app/Library/Captcha.php
@@ -0,0 +1,139 @@
+cache = Di::getDefault()->get('cache');
+ }
+
+ public function generate()
+ {
+ $ticket = $this->getRandTicket();
+
+ $cacheKey = $this->getCacheKey($ticket);
+
+ $expression = $this->getExpression();
+
+ $this->cache->save($cacheKey, $expression['result'], 600);
+
+ $width = 100;
+ $height = 25;
+
+ $im = imagecreate($width, $height);
+
+ $white = imagecolorallocate($im, 255, 255, 255);
+ $gray = imagecolorallocate($im, 118, 151, 199);
+ $bgColor = imagecolorallocate($im, rand(0, 100), rand(0, 100), rand(0, 100));
+
+ imagefilledrectangle($im, 0, 0, $width, $height, $bgColor);
+
+ for ($i = 0; $i < 200; $i++) {
+ imagesetpixel($im, rand(0, $width), rand(0, $height), $gray);
+ }
+
+ imagestring($im, 5, 5, 4, $expression['num1'], $white);
+ imagestring($im, 5, 30, 3, $expression['operator'], $white);
+ imagestring($im, 5, 45, 4, $expression['num2'], $white);
+ imagestring($im, 5, 70, 3, '=', $white);
+ imagestring($im, 5, 85, 3, '?', $white);
+
+ ob_start();
+
+ imagepng($im);
+
+ $content = ob_get_clean();
+
+ imagedestroy($im);
+
+ return [
+ 'ticket' => $ticket,
+ 'content' => $this->base64Encode($content),
+ ];
+ }
+
+ public function check($ticket, $rand)
+ {
+ if (!$ticket) return false;
+
+ if (!$rand) return false;
+
+ $key = $this->getCacheKey($ticket);
+
+ $content = $this->cache->get($key);
+
+ return $content == $rand;
+ }
+
+ protected function getExpression()
+ {
+ $operators = ['+', '-', '*', '/'];
+
+ $index = array_rand($operators);
+
+ $operator = $operators[$index];
+
+ switch ($operator) {
+ case '+':
+ $num1 = rand(10, 50);
+ $num2 = rand(10, 50);
+ $result = $num1 + $num2;
+ break;
+ case '-':
+ $num1 = rand(50, 100);
+ $num2 = rand(10, 50);
+ $result = $num1 - $num2;
+ break;
+ case '*':
+ $num1 = rand(1, 10);
+ $num2 = rand(1, 10);
+ $result = $num1 * $num2;
+ break;
+ default:
+ $multiple = rand(2, 10);
+ $num1 = $multiple * rand(1, 10);
+ $num2 = $multiple;
+ $result = $num1 / $num2;
+ break;
+ }
+
+ return [
+ 'num1' => $num1,
+ 'num2' => $num2,
+ 'operator' => $operator,
+ 'result' => $result,
+ ];
+ }
+
+ protected function base64Encode($content)
+ {
+ return sprintf('data:image/png;base64,%s', base64_encode($content));
+ }
+
+ protected function getRandTicket()
+ {
+ return Text::random(Text::RANDOM_ALNUM, 16);
+ }
+
+ protected function getCacheKey($key)
+ {
+ return "captcha:{$key}";
+ }
+
+}
\ No newline at end of file
diff --git a/app/Services/Captcha.php b/app/Services/Captcha.php
deleted file mode 100644
index 950fba0e..00000000
--- a/app/Services/Captcha.php
+++ /dev/null
@@ -1,131 +0,0 @@
-settings = $this->getSettings('captcha');
-
- $this->logger = $this->getLogger('captcha');
-
- $this->client = $this->getCaptchaClient();
- }
-
- /**
- * 校验验证码
- *
- * @param string $ticket
- * @param string $rand
- * @return bool
- */
- function verify($ticket, $rand)
- {
- $userIp = $this->request->getClientAddress();
-
- $appId = $this->settings['app_id'];
- $secretKey = $this->settings['secret_key'];
- $captchaType = 9;
-
- try {
-
- $request = new DescribeCaptchaResultRequest();
-
- /**
- * 注意:CaptchaType和CaptchaAppId强类型要求
- */
- $params = json_encode([
- 'Ticket' => $ticket,
- 'Randstr' => $rand,
- 'UserIp' => $userIp,
- 'CaptchaType' => (int)$captchaType,
- 'CaptchaAppId' => (int)$appId,
- 'AppSecretKey' => $secretKey,
- ]);
-
- $request->fromJsonString($params);
-
- $this->logger->debug('Describe Captcha Result Request ' . $params);
-
- $response = $this->client->DescribeCaptchaResult($request);
-
- $this->logger->debug('Describe Captcha Result Response ' . $response->toJsonString());
-
- $data = json_decode($response->toJsonString(), true);
-
- $result = $data['CaptchaCode'] == 1;
-
- } catch (TencentCloudSDKException $e) {
-
- $this->logger->error('Describe Captcha Result Exception ' . kg_json_encode([
- 'code' => $e->getErrorCode(),
- 'message' => $e->getMessage(),
- 'requestId' => $e->getRequestId(),
- ]));
-
- $result = false;
- }
-
- return $result;
- }
-
- /**
- * 获取CaptchaClient
- *
- * @return CaptchaClient
- */
- public function getCaptchaClient()
- {
- $secret = $this->getSettings('secret');
-
- $secretId = $secret['secret_id'];
- $secretKey = $secret['secret_key'];
-
- $region = $this->settings['region'] ?? 'ap-guangzhou';
-
- $credential = new Credential($secretId, $secretKey);
-
- $httpProfile = new HttpProfile();
-
- $httpProfile->setEndpoint(self::END_POINT);
-
- $clientProfile = new ClientProfile();
-
- $clientProfile->setHttpProfile($httpProfile);
-
- return new CaptchaClient($credential, $region, $clientProfile);
- }
-
-}
diff --git a/app/Services/Logic/Account/EmailUpdate.php b/app/Services/Logic/Account/EmailUpdate.php
index 066b4127..ebb2d6cc 100644
--- a/app/Services/Logic/Account/EmailUpdate.php
+++ b/app/Services/Logic/Account/EmailUpdate.php
@@ -33,7 +33,12 @@ class EmailUpdate extends LogicService
$accountValidator->checkIfEmailTaken($post['email']);
}
- $accountValidator->checkLoginPassword($account, $post['login_password']);
+ /**
+ * 未设置过密码不检查原密码
+ */
+ if (!empty($account->password)) {
+ $accountValidator->checkLoginPassword($account, $post['login_password']);
+ }
$verifyValidator = new VerifyValidator();
diff --git a/app/Services/Logic/Account/PhoneUpdate.php b/app/Services/Logic/Account/PhoneUpdate.php
index 19ba070f..46389a0d 100644
--- a/app/Services/Logic/Account/PhoneUpdate.php
+++ b/app/Services/Logic/Account/PhoneUpdate.php
@@ -33,7 +33,12 @@ class PhoneUpdate extends LogicService
$accountValidator->checkIfPhoneTaken($post['phone']);
}
- $accountValidator->checkLoginPassword($account, $post['login_password']);
+ /**
+ * 未设置过密码不检查原密码
+ */
+ if (!empty($account->password)) {
+ $accountValidator->checkLoginPassword($account, $post['login_password']);
+ }
$verifyValidator = new VerifyValidator();
diff --git a/app/Services/Logic/Verify/Captcha.php b/app/Services/Logic/Verify/Captcha.php
new file mode 100644
index 00000000..3c831910
--- /dev/null
+++ b/app/Services/Logic/Verify/Captcha.php
@@ -0,0 +1,23 @@
+generate();
+ }
+
+}
diff --git a/app/Services/Logic/Verify/Code.php b/app/Services/Logic/Verify/Code.php
new file mode 100644
index 00000000..2a95a2a4
--- /dev/null
+++ b/app/Services/Logic/Verify/Code.php
@@ -0,0 +1,42 @@
+request->getPost();
+
+ $verifyValidator = new VerifyValidator();
+ $captchaValidator = new CaptchaValidator();
+
+ $captchaValidator->checkCode($post['ticket'], $post['rand']);
+
+ $isMail = CommonValidator::email($post['account']);
+
+ if ($isMail) {
+ $account = $verifyValidator->checkEmail($post['account']);
+ $service = new MailVerifyService();
+ } else {
+ $account = $verifyValidator->checkPhone($post['account']);
+ $service = new SmsVerifyService();
+ }
+
+ $service->handle($account);
+ }
+
+}
diff --git a/app/Services/Logic/Verify/MailCode.php b/app/Services/Logic/Verify/MailCode.php
index 48b317b7..ff68b47e 100644
--- a/app/Services/Logic/Verify/MailCode.php
+++ b/app/Services/Logic/Verify/MailCode.php
@@ -21,20 +21,15 @@ class MailCode extends LogicService
$validator = new VerifyValidator();
- $post['email'] = $validator->checkEmail($post['email']);
+ $email = $validator->checkEmail($post['email']);
- $captcha = $this->getSettings('captcha');
+ $validator = new CaptchaValidator();
- if ($captcha['enabled'] == 1) {
-
- $validator = new CaptchaValidator();
-
- $validator->checkCode($post['captcha']['ticket'], $post['captcha']['rand']);
- }
+ $validator->checkCode($post['ticket'], $post['rand']);
$service = new MailVerifyService();
- $service->handle($post['email']);
+ $service->handle($email);
}
}
diff --git a/app/Services/Logic/Verify/SmsCode.php b/app/Services/Logic/Verify/SmsCode.php
index b7ad8c71..c95192e5 100644
--- a/app/Services/Logic/Verify/SmsCode.php
+++ b/app/Services/Logic/Verify/SmsCode.php
@@ -21,20 +21,15 @@ class SmsCode extends LogicService
$validator = new VerifyValidator();
- $post['phone'] = $validator->checkPhone($post['phone']);
+ $phone = $validator->checkPhone($post['phone']);
- $captcha = $this->getSettings('captcha');
+ $validator = new CaptchaValidator();
- if ($captcha['enabled'] == 1) {
-
- $validator = new CaptchaValidator();
-
- $validator->checkCode($post['captcha']['ticket'], $post['captcha']['rand']);
- }
+ $validator->checkCode($post['ticket'], $post['rand']);
$service = new SmsVerifyService();
- $service->handle($post['phone']);
+ $service->handle($phone);
}
}
diff --git a/app/Validators/Captcha.php b/app/Validators/Captcha.php
index 401ef702..030ada6f 100644
--- a/app/Validators/Captcha.php
+++ b/app/Validators/Captcha.php
@@ -8,16 +8,16 @@
namespace App\Validators;
use App\Exceptions\BadRequest as BadRequestException;
-use App\Services\Captcha as CaptchaService;
+use App\Library\Captcha as ImageCaptcha;
class Captcha extends Validator
{
public function checkCode($ticket, $rand)
{
- $service = new CaptchaService();
+ $captcha = new ImageCaptcha();
- $result = $service->verify($ticket, $rand);
+ $result = $captcha->check($ticket, $rand);
if (!$result) {
throw new BadRequestException('captcha.invalid_code');
diff --git a/composer.json b/composer.json
index 2b0f0bc9..10ea446a 100644
--- a/composer.json
+++ b/composer.json
@@ -5,6 +5,7 @@
"ext-pdo": "*",
"ext-json": "*",
"ext-fileinfo": "*",
+ "ext-gd": "*",
"phalcon/incubator": "^3.4",
"guzzlehttp/guzzle": "^6.5",
"swiftmailer/swiftmailer": "^6.0",
diff --git a/db/migrations/20230625182830.php b/db/migrations/20230625182830.php
new file mode 100644
index 00000000..e91f0a00
--- /dev/null
+++ b/db/migrations/20230625182830.php
@@ -0,0 +1,26 @@
+deleteCaptchaSettings();
+ }
+
+ protected function deleteCaptchaSettings()
+ {
+ $this->getQueryBuilder()
+ ->delete('kg_setting')
+ ->where(['section' => 'captcha'])
+ ->execute();
+ }
+
+}
diff --git a/public/static/home/js/captcha.login.js b/public/static/home/js/captcha.login.js
deleted file mode 100644
index 245c27c9..00000000
--- a/public/static/home/js/captcha.login.js
+++ /dev/null
@@ -1,20 +0,0 @@
-layui.use(['jquery'], function () {
-
- var $ = layui.jquery;
-
- if ($('#cl-captcha-enabled').val() === '1') {
- var captcha = new TencentCaptcha(
- $('#cl-emit-btn')[0],
- $('#cl-captcha-appId').val(),
- function (res) {
- if (res.ret === 0) {
- $('#cl-captcha-ticket').val(res.ticket);
- $('#cl-captcha-rand').val(res.randstr);
- $('#cl-submit-btn').removeClass('layui-btn-disabled').removeAttr('disabled');
- $('#captcha-block').hide();
- }
- }
- );
- }
-
-});
\ No newline at end of file
diff --git a/public/static/home/js/captcha.verify.email.js b/public/static/home/js/captcha.verify.email.js
index 8ab8f11b..8dd02d81 100644
--- a/public/static/home/js/captcha.verify.email.js
+++ b/public/static/home/js/captcha.verify.email.js
@@ -1,80 +1,30 @@
-layui.use(['jquery', 'layer', 'util', 'helper'], function () {
+layui.use(['jquery', 'layer', 'helper'], function () {
var $ = layui.jquery;
var layer = layui.layer;
- var util = layui.util;
var helper = layui.helper;
- var timeCounting = false;
var $account = $('#cv-email');
var $emit = $('#cv-email-emit-btn');
- var $submit = $('#cv-email-submit-btn');
- if ($('#cv-email-captcha-enabled').val() === '1') {
- var captcha = new TencentCaptcha(
- $emit[0],
- $('#cv-email-captcha-appId').val(),
- function (res) {
- if (res.ret === 0) {
- $('#cv-email-captcha-ticket').val(res.ticket);
- $('#cv-email-captcha-rand').val(res.randstr);
- sendVerifyCode();
- }
- }
- );
- } else {
- $emit.on('click', function () {
- sendVerifyCode();
+ $emit.on('click', function () {
+ var url = '/verify/captcha?type=mail&account=' + $account.val();
+ layer.open({
+ type: 2,
+ title: '获取验证码',
+ area: ['500px', '250px'],
+ content: [url, 'no'],
});
- }
+ });
$account.on('keyup', function () {
var account = $(this).val();
var accountOk = helper.isEmail(account);
- if (accountOk && !timeCounting) {
+ if (accountOk) {
$emit.removeClass('layui-btn-disabled').removeAttr('disabled');
} else {
$emit.addClass('layui-btn-disabled').attr('disabled', 'disabled');
}
});
- function sendVerifyCode() {
- if (helper.isEmail($account.val())) {
- var postUrl = '/verify/mail/code';
- var postData = {
- email: $account.val(),
- captcha: {
- ticket: $('#cv-email-captcha-ticket').val(),
- rand: $('#cv-email-captcha-rand').val(),
- }
- };
- $.ajax({
- type: 'POST',
- url: postUrl,
- data: postData,
- success: function () {
- layer.msg('发送验证码成功', {icon: 1});
- }
- });
- $submit.removeClass('layui-btn-disabled').removeAttr('disabled');
- $emit.addClass('layui-btn-disabled').attr('disabled', 'disabled');
- showCountDown($emit);
- }
- }
-
- function showCountDown() {
- var serverTime = new Date().getTime();
- var endTime = serverTime + 60 * 1000;
- util.countdown(endTime, serverTime, function (date, serverTime, timer) {
- var left = date[0] * 86400 + date[1] * 3600 + date[2] * 60 + date[3];
- $emit.text(left + '秒');
- if (left === 0) {
- $emit.removeClass('layui-btn-disabled').removeAttr('disabled').text('重新发送');
- clearInterval(timer);
- timeCounting = false;
- }
- });
- timeCounting = true;
- }
-
});
\ No newline at end of file
diff --git a/public/static/home/js/captcha.verify.js b/public/static/home/js/captcha.verify.js
index a8bcf813..afa9f7b1 100644
--- a/public/static/home/js/captcha.verify.js
+++ b/public/static/home/js/captcha.verify.js
@@ -1,86 +1,30 @@
-layui.use(['jquery', 'layer', 'util', 'helper'], function () {
+layui.use(['jquery', 'layer', 'helper'], function () {
var $ = layui.jquery;
var layer = layui.layer;
- var util = layui.util;
var helper = layui.helper;
- var timeCounting = false;
var $account = $('#cv-account');
var $emit = $('#cv-emit-btn');
- var $submit = $('#cv-submit-btn');
- if ($('#cv-captcha-enabled').val() === '1') {
- var captcha = new TencentCaptcha(
- $emit[0],
- $('#cv-captcha-appId').val(),
- function (res) {
- if (res.ret === 0) {
- $('#cv-captcha-ticket').val(res.ticket);
- $('#cv-captcha-rand').val(res.randstr);
- sendVerifyCode();
- }
- }
- );
- } else {
- $emit.on('click', function () {
- sendVerifyCode();
+ $emit.on('click', function () {
+ var url = '/verify/captcha?type=all&account=' + $account.val();
+ layer.open({
+ type: 2,
+ title: '获取验证码',
+ area: ['500px', '250px'],
+ content: [url, 'no'],
});
- }
+ });
$account.on('keyup', function () {
var account = $(this).val();
var accountOk = helper.isPhone(account) || helper.isEmail(account);
- if (accountOk && !timeCounting) {
+ if (accountOk) {
$emit.removeClass('layui-btn-disabled').removeAttr('disabled');
} else {
$emit.addClass('layui-btn-disabled').attr('disabled', 'disabled');
}
});
- function sendVerifyCode() {
- if (helper.isEmail($account.val()) || helper.isPhone($account.val())) {
- var postUrl;
- var postData = {
- captcha: {
- ticket: $('#cv-captcha-ticket').val(),
- rand: $('#cv-captcha-rand').val(),
- }
- };
- if (helper.isPhone($account.val())) {
- postData.phone = $account.val();
- postUrl = '/verify/sms/code';
- } else if (helper.isEmail($account.val())) {
- postData.email = $account.val();
- postUrl = '/verify/mail/code';
- }
- $.ajax({
- type: 'POST',
- url: postUrl,
- data: postData,
- success: function () {
- layer.msg('发送验证码成功', {icon: 1});
- }
- });
- $submit.removeClass('layui-btn-disabled').removeAttr('disabled');
- $emit.addClass('layui-btn-disabled').attr('disabled', 'disabled');
- showCountDown($emit);
- }
- }
-
- function showCountDown() {
- var serverTime = new Date().getTime();
- var endTime = serverTime + 60 * 1000;
- util.countdown(endTime, serverTime, function (date, serverTime, timer) {
- var left = date[0] * 86400 + date[1] * 3600 + date[2] * 60 + date[3];
- $emit.text(left + '秒');
- if (left === 0) {
- $emit.removeClass('layui-btn-disabled').removeAttr('disabled').text('重新发送');
- clearInterval(timer);
- timeCounting = false;
- }
- });
- timeCounting = true;
- }
-
});
\ No newline at end of file
diff --git a/public/static/home/js/captcha.verify.phone.js b/public/static/home/js/captcha.verify.phone.js
index 95f0a731..a2a36438 100644
--- a/public/static/home/js/captcha.verify.phone.js
+++ b/public/static/home/js/captcha.verify.phone.js
@@ -1,80 +1,30 @@
-layui.use(['jquery', 'layer', 'util', 'helper'], function () {
+layui.use(['jquery', 'layer', 'helper'], function () {
var $ = layui.jquery;
var layer = layui.layer;
- var util = layui.util;
var helper = layui.helper;
- var timeCounting = false;
var $account = $('#cv-phone');
var $emit = $('#cv-phone-emit-btn');
- var $submit = $('#cv-phone-submit-btn');
- if ($('#cv-phone-captcha-enabled').val() === '1') {
- var captcha = new TencentCaptcha(
- $emit[0],
- $('#cv-phone-captcha-appId').val(),
- function (res) {
- if (res.ret === 0) {
- $('#cv-phone-captcha-ticket').val(res.ticket);
- $('#cv-phone-captcha-rand').val(res.randstr);
- sendVerifyCode();
- }
- }
- );
- } else {
- $emit.on('click', function () {
- sendVerifyCode();
+ $emit.on('click', function () {
+ var url = '/verify/captcha?type=phone&account=' + $account.val();
+ layer.open({
+ type: 2,
+ title: '获取验证码',
+ area: ['500px', '250px'],
+ content: [url, 'no'],
});
- }
+ });
$account.on('keyup', function () {
var account = $(this).val();
var accountOk = helper.isPhone(account);
- if (accountOk && !timeCounting) {
+ if (accountOk) {
$emit.removeClass('layui-btn-disabled').removeAttr('disabled');
} else {
$emit.addClass('layui-btn-disabled').attr('disabled', 'disabled');
}
});
- function sendVerifyCode() {
- if (helper.isPhone($account.val())) {
- var postUrl = '/verify/sms/code';
- var postData = {
- phone: $account.val(),
- captcha: {
- ticket: $('#cv-phone-captcha-ticket').val(),
- rand: $('#cv-phone-captcha-rand').val(),
- }
- };
- $.ajax({
- type: 'POST',
- url: postUrl,
- data: postData,
- success: function () {
- layer.msg('发送验证码成功', {icon: 1});
- }
- });
- $submit.removeClass('layui-btn-disabled').removeAttr('disabled');
- $emit.addClass('layui-btn-disabled').attr('disabled', 'disabled');
- showCountDown($emit);
- }
- }
-
- function showCountDown() {
- var serverTime = new Date().getTime();
- var endTime = serverTime + 60 * 1000;
- util.countdown(endTime, serverTime, function (date, serverTime, timer) {
- var left = date[0] * 86400 + date[1] * 3600 + date[2] * 60 + date[3];
- $emit.text(left + '秒');
- if (left === 0) {
- $emit.removeClass('layui-btn-disabled').removeAttr('disabled').text('重新发送');
- clearInterval(timer);
- timeCounting = false;
- }
- });
- timeCounting = true;
- }
-
});
\ No newline at end of file