From a7d47cefe3d69f9b1ac8f137b5cdda22ae697201 Mon Sep 17 00:00:00 2001 From: Anyon Date: Wed, 25 Sep 2019 11:36:11 +0800 Subject: [PATCH] =?UTF-8?q?[=E6=9B=B4=E6=96=B0]=E4=BF=AE=E6=94=B9=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E7=99=BB=E5=BD=95=EF=BC=88=E5=A2=9E=E5=8A=A0=E5=9B=BE?= =?UTF-8?q?=E5=BD=A2=E9=AA=8C=E8=AF=81=E7=A0=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/admin/controller/Login.php | 35 +---- application/admin/service/CaptchaService.php | 146 +++++++++++++++++++ application/admin/service/font/icon.ttf | Bin 0 -> 34852 bytes application/admin/sys.php | 8 + application/admin/view/login/index.html | 12 +- composer.json | 1 + public/static/admin.js | 23 ++- public/static/theme/css/login.css | 15 ++ public/static/theme/css/login.css.map | 2 +- public/static/theme/css/login.less | 20 +++ vendor/autoload.php | 2 +- vendor/composer/autoload_real.php | 14 +- vendor/composer/autoload_static.php | 8 +- 13 files changed, 242 insertions(+), 44 deletions(-) create mode 100644 application/admin/service/CaptchaService.php create mode 100644 application/admin/service/font/icon.ttf diff --git a/application/admin/controller/Login.php b/application/admin/controller/Login.php index fa24db16a..76ebf256c 100644 --- a/application/admin/controller/Login.php +++ b/application/admin/controller/Login.php @@ -15,6 +15,7 @@ namespace app\admin\controller; +use app\admin\service\CaptchaService; use app\admin\service\NodeService; use library\Controller; use think\Db; @@ -46,6 +47,7 @@ class Login extends Controller $this->domain = Request::host(true); if (!($this->loginskey = session('loginskey'))) session('loginskey', $this->loginskey = uniqid()); $this->devmode = in_array($this->domain, ['127.0.0.1', 'localhost']) || is_numeric(stripos($this->domain, 'thinkadmin.top')); + $this->captcha = new CaptchaService(); $this->fetch(); } } else { @@ -59,40 +61,19 @@ class Login extends Controller 'username.min' => '登录账号长度不能少于4位有效字符!', 'password.min' => '登录密码长度不能少于4位有效字符!', ]); + if (!CaptchaService::check(input('verify'), input('uniqid'))) { + $this->error('图形验证码验证失败,请重新输入!'); + } // 用户信息验证 $map = ['is_deleted' => '0', 'username' => $data['username']]; $user = Db::name('SystemUser')->where($map)->order('id desc')->find(); if (empty($user)) $this->error('登录账号或密码错误,请重新输入!'); - if (empty($user['status'])) $this->error('账号已经被禁用,请联系管理员!'); - // 账号锁定消息 - $cache = cache("user_login_{$user['username']}"); - if (is_array($cache) && !empty($cache['number']) && !empty($cache['time'])) { - if ($cache['number'] >= 10 && ($diff = $cache['time'] + 3600 - time()) > 0) { - list($m, $s, $info) = [floor($diff / 60), floor($diff % 60), '']; - if ($m > 0) $info = "{$m} 分"; - $this->error("抱歉,该账号已经被锁定!

连续 10 次登录错误,请 {$info} {$s} 秒后再登录!

"); - } - } if (md5($user['password'] . session('loginskey')) !== $data['password']) { - if (empty($cache) || empty($cache['time']) || empty($cache['number']) || $cache['time'] + 3600 < time()) { - $cache = ['time' => time(), 'number' => 1, 'geoip' => $this->request->ip()]; - } elseif ($cache['number'] + 1 <= 10) { - $cache = ['time' => time(), 'number' => $cache['number'] + 1, 'geoip' => $this->request->ip()]; - } - cache("user_login_{$user['username']}", $cache); - if (($diff = 10 - $cache['number']) > 0) { - $this->error("登录账号或密码错误!

还有 {$diff} 次尝试机会,将锁定一小时内禁止登录!

"); - } else { - sysoplog('系统管理', "账号{$user['username']}连续10次登录密码错误,请注意账号安全!"); - $this->error("登录账号或密码错误!

尝试次数达到上限,锁定一小时内禁止登录!

"); - } + $this->error('登录账号或密码错误,请重新输入!'); } - // 登录成功并更新账号 - cache("user_login_{$user['username']}", null); + if (empty($user['status'])) $this->error('账号已经被禁用,请联系管理员!'); Db::name('SystemUser')->where(['id' => $user['id']])->update([ - 'login_at' => Db::raw('now()'), - 'login_ip' => $this->request->ip(), - 'login_num' => Db::raw('login_num+1'), + 'login_at' => Db::raw('now()'), 'login_ip' => Request::ip(), 'login_num' => Db::raw('login_num+1'), ]); session('loginskey', null); session('admin_user', $user); diff --git a/application/admin/service/CaptchaService.php b/application/admin/service/CaptchaService.php new file mode 100644 index 000000000..f8bd8e6d3 --- /dev/null +++ b/application/admin/service/CaptchaService.php @@ -0,0 +1,146 @@ + $v) if (isset($this->$k)) $this->$k = $v; + // 设置字体文件路径 + $this->font = __DIR__ . '/font/icon.ttf'; + // 生成验证码序号 + $this->uniqid = uniqid('captcha'); + // 生成验证码字符串 + $_len = strlen($this->charset) - 1; + for ($i = 0; $i < $this->codelen; $i++) { + $this->code .= $this->charset[mt_rand(0, $_len)]; + } + // 缓存验证码字符串 + Cache::tag('captcha')->set($this->uniqid, $this->code, 360); + p([$this->uniqid, $this->code]); + } + + /** + * 创建验证码图片 + * @return string + */ + private function createImage() + { + // 生成背景 + $this->img = imagecreatetruecolor($this->width, $this->height); + $color = imagecolorallocate($this->img, mt_rand(220, 255), mt_rand(220, 255), mt_rand(220, 255)); + imagefilledrectangle($this->img, 0, $this->height, $this->width, 0, $color); + // 线条 + for ($i = 0; $i < 6; $i++) { + $color = imagecolorallocate($this->img, mt_rand(0, 50), mt_rand(0, 50), mt_rand(0, 50)); + imageline($this->img, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $color); + } + // 雪花 + for ($i = 0; $i < 100; $i++) { + $color = imagecolorallocate($this->img, mt_rand(200, 255), mt_rand(200, 255), mt_rand(200, 255)); + imagestring($this->img, mt_rand(1, 5), mt_rand(0, $this->width), mt_rand(0, $this->height), '*', $color); + } + // 生成文字 + $_x = $this->width / $this->codelen; + for ($i = 0; $i < $this->codelen; $i++) { + $this->fontcolor = imagecolorallocate($this->img, mt_rand(0, 156), mt_rand(0, 156), mt_rand(0, 156)); + imagettftext($this->img, $this->fontsize, mt_rand(-30, 30), $_x * $i + mt_rand(1, 5), $this->height / 1.4, $this->fontcolor, $this->font, $this->code[$i]); + } + ob_start(); + imagepng($this->img); + $data = ob_get_contents(); + ob_end_clean(); + imagedestroy($this->img); + return base64_encode($data); + } + + /** + * 获取验证码 + * @return array + */ + public function getAttr() + { + return ['code' => $this->code, 'uniq' => $this->uniqid, 'data' => $this->getData()]; + } + + /** + * 获取验证码值 + * @return string + */ + public function getCode() + { + return $this->code; + } + + /** + * 获取图片内容 + * @return string + */ + public function getData() + { + return "data:image/png;base64,{$this->createImage()}"; + } + + /** + * 获取验证码编号 + * @return string + */ + public function getUniqid() + { + return $this->uniqid; + } + + /** + * 检查验证码是否正确 + * @param string $code 需要验证的值 + * @param string $uniqid 验证码编号 + * @return boolean + */ + public static function check($code, $uniqid = null) + { + $_uni = is_string($uniqid) ? $uniqid : input('uniqid', '-'); + $_ver = Cache::tag('captcha')->get($_uni); + if (is_string($_ver) && strtolower($_ver) === strtolower($code)) { + Cache::tag('captcha')->rm($_uni); + return true; + } else return false; + } +} diff --git a/application/admin/service/font/icon.ttf b/application/admin/service/font/icon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..54a14ed1c367db3c2680109c712e15e886b3937c GIT binary patch literal 34852 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 c%R7_2IG>vddcb)xU-zgoC({L12w|r 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 $image->getUniqid(), 'image' => $image->getData()]); +}); \ No newline at end of file diff --git a/application/admin/view/login/index.html b/application/admin/view/login/index.html index c8461b4cd..b5031dfe6 100644 --- a/application/admin/view/login/index.html +++ b/application/admin/view/login/index.html @@ -16,15 +16,23 @@
  • +
  • + + img + +
  • diff --git a/composer.json b/composer.json index cff468af2..a29311e1f 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ ], "require": { "php": ">=5.6", + "ext-gd": "*", "ext-curl": "*", "ext-json": "*", "ext-soap": "*", diff --git a/public/static/admin.js b/public/static/admin.js index 495b51fe4..6ab481f4c 100644 --- a/public/static/admin.js +++ b/public/static/admin.js @@ -747,16 +747,35 @@ $(function () { }); /*! 后台加密登录处理 */ - $body.find('[data-login-form]').map(function () { + $body.find('[data-login-form]').map(function (that) { + that = this; require(["md5"], function (md5) { $("form").vali(function (data) { data['password'] = md5.hash(md5.hash(data['password']) + data['skey']); if (data['skey']) delete data['skey']; - $.form.load(location.href, data, "post", null, null, null, 'false'); + $.form.load(location.href, data, "post", function (ret) { + if (parseInt(ret.code) !== 1) { + $(that).find('[name=verify]').val(''); + $(that).find('.verify.layui-hide').removeClass('layui-hide'); + $(that).find('[data-refresh-captcha]').trigger('click'); + } + }, null, null, 'false'); }); }); }); + /*! 后台图形验证码刷新 */ + $body.on('click', '[data-refresh-captcha]', function (image, verify, uniqid) { + image = this, uniqid = this.getAttribute('data-uniqid-field') || 'uniqid'; + verify = this.getAttribute('data-refresh-captcha') || this.getAttribute('data-verify-field') || 'verify'; + $.form.load('?s=think/admin/captcha', {}, 'get', function (ret) { + image.src = ret.image; + $(image).parents('form').find('[name=' + verify + ']').val(''); + $(image).parents('form').find('[name=' + uniqid + ']').val(ret.uniqid); + return false; + }, false); + }); + /*! 图片加载异常处理 */ document.addEventListener('error', function (e, elem) { elem = e.target; diff --git a/public/static/theme/css/login.css b/public/static/theme/css/login.css index f02acda6a..d02817adc 100644 --- a/public/static/theme/css/login.css +++ b/public/static/theme/css/login.css @@ -64,6 +64,21 @@ body { margin-top: 20px; text-shadow: #000 0.1em 0.1em 0.1em; } +.login-container form ul li.verify label { + width: 200px; +} +.login-container form ul li.verify input.layui-input { + text-transform: uppercase; +} +.login-container form ul li.verify img { + width: 95px; + height: 44px; + cursor: pointer; + position: absolute; + margin-left: 5px; + border-radius: 5px; + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.1); +} .login-container form ul li i.layui-icon { color: #fff; font-size: 18px; diff --git a/public/static/theme/css/login.css.map b/public/static/theme/css/login.css.map index 2caebbbf1..b3793e5ac 100644 --- a/public/static/theme/css/login.css.map +++ b/public/static/theme/css/login.css.map @@ -1 +1 @@ -{"version":3,"sources":["login.less"],"names":[],"mappings":"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","file":"login.css"} \ No newline at end of file +{"version":3,"sources":["login.less"],"names":[],"mappings":"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","file":"login.css"} \ No newline at end of file diff --git a/public/static/theme/css/login.less b/public/static/theme/css/login.less index eafd2d419..84b9b225a 100644 --- a/public/static/theme/css/login.less +++ b/public/static/theme/css/login.less @@ -85,6 +85,26 @@ body { margin-top: 20px; text-shadow: #000 .1em .1em .1em; + &.verify { + label { + width: 200px; + } + + input.layui-input { + text-transform: uppercase + } + + img { + width: 95px; + height: 44px; + cursor: pointer; + position: absolute; + margin-left: 5px; + border-radius: 5px; + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .1); + } + } + i.layui-icon { color: #fff; font-size: 18px; diff --git a/vendor/autoload.php b/vendor/autoload.php index 85197121d..163fb58b2 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit415d1b9a7b2ee3822de734e6cbe4a804::getLoader(); +return ComposerAutoloaderInitea9430bef9688db72c38464c06dd4032::getLoader(); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 4555f2fc0..834102d8d 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit415d1b9a7b2ee3822de734e6cbe4a804 +class ComposerAutoloaderInitea9430bef9688db72c38464c06dd4032 { private static $loader; @@ -19,15 +19,15 @@ class ComposerAutoloaderInit415d1b9a7b2ee3822de734e6cbe4a804 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit415d1b9a7b2ee3822de734e6cbe4a804', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitea9430bef9688db72c38464c06dd4032', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit415d1b9a7b2ee3822de734e6cbe4a804', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitea9430bef9688db72c38464c06dd4032', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit415d1b9a7b2ee3822de734e6cbe4a804::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInitea9430bef9688db72c38464c06dd4032::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -48,19 +48,19 @@ class ComposerAutoloaderInit415d1b9a7b2ee3822de734e6cbe4a804 $loader->register(true); if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit415d1b9a7b2ee3822de734e6cbe4a804::$files; + $includeFiles = Composer\Autoload\ComposerStaticInitea9430bef9688db72c38464c06dd4032::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire415d1b9a7b2ee3822de734e6cbe4a804($fileIdentifier, $file); + composerRequireea9430bef9688db72c38464c06dd4032($fileIdentifier, $file); } return $loader; } } -function composerRequire415d1b9a7b2ee3822de734e6cbe4a804($fileIdentifier, $file) +function composerRequireea9430bef9688db72c38464c06dd4032($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 3582c5660..0ecc870a8 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit415d1b9a7b2ee3822de734e6cbe4a804 +class ComposerStaticInitea9430bef9688db72c38464c06dd4032 { public static $files = array ( '841780ea2e1d6545ea3a253239d59c05' => __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu/functions.php', @@ -321,9 +321,9 @@ class ComposerStaticInit415d1b9a7b2ee3822de734e6cbe4a804 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit415d1b9a7b2ee3822de734e6cbe4a804::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit415d1b9a7b2ee3822de734e6cbe4a804::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit415d1b9a7b2ee3822de734e6cbe4a804::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInitea9430bef9688db72c38464c06dd4032::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitea9430bef9688db72c38464c06dd4032::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitea9430bef9688db72c38464c06dd4032::$classMap; }, null, ClassLoader::class); }