mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-26 20:52:44 +08:00
Merge branch 'koogua/block-flood-login' into demo
This commit is contained in:
commit
c2c1c9987e
@ -97,7 +97,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set share_url = full_url({'for':'home.share'},{'id':article.id,'type':'article','referer':auth_user.id}) %}
|
{% set share_url = full_url({'for':'home.share'},{'id':article.id,'type':'article'}) %}
|
||||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
||||||
|
|
||||||
<div class="layui-hide">
|
<div class="layui-hide">
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
<input type="hidden" name="bind_user_url" value='{{ bind_user_url }}'>
|
<input type="hidden" name="bind_user_url" value='{{ bind_user_url }}'>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter','referer':auth_user.id}) %}
|
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter'}) %}
|
||||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
||||||
|
|
||||||
<div class="layui-hide">
|
<div class="layui-hide">
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
<input type="hidden" name="chapter.learning_url" value="{{ learning_url }}">
|
<input type="hidden" name="chapter.learning_url" value="{{ learning_url }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter','referer':auth_user.id}) %}
|
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter'}) %}
|
||||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
||||||
|
|
||||||
<div class="layui-hide">
|
<div class="layui-hide">
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
<input type="hidden" name="chapter.play_urls" value='{{ chapter.play_urls|json_encode }}'>
|
<input type="hidden" name="chapter.play_urls" value='{{ chapter.play_urls|json_encode }}'>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter','referer':auth_user.id}) %}
|
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter'}) %}
|
||||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
||||||
|
|
||||||
<div class="layui-hide">
|
<div class="layui-hide">
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set share_url = full_url({'for':'home.share'},{'id':course.id,'type':'course','referer':auth_user.id}) %}
|
{% set share_url = full_url({'for':'home.share'},{'id':course.id,'type':'course'}) %}
|
||||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
||||||
|
|
||||||
<div class="layui-hide">
|
<div class="layui-hide">
|
||||||
|
@ -97,7 +97,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set share_url = full_url({'for':'home.share'},{'id':question.id,'type':'question','referer':auth_user.id}) %}
|
{% set share_url = full_url({'for':'home.share'},{'id':question.id,'type':'question'}) %}
|
||||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
||||||
|
|
||||||
<div class="layui-hide">
|
<div class="layui-hide">
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set share_url = full_url({'for':'home.share'},{'id':user.id,'type':'user','referer':auth_user.id}) %}
|
{% set share_url = full_url({'for':'home.share'},{'id':user.id,'type':'user'}) %}
|
||||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
|
||||||
|
|
||||||
<div class="layui-hide">
|
<div class="layui-hide">
|
||||||
|
@ -16,7 +16,7 @@ class AppInfo
|
|||||||
|
|
||||||
protected $link = 'https://koogua.com';
|
protected $link = 'https://koogua.com';
|
||||||
|
|
||||||
protected $version = '1.4.6';
|
protected $version = '1.4.7';
|
||||||
|
|
||||||
public function __get($name)
|
public function __get($name)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||||
|
|
||||||
class UserSession extends Model
|
class UserSession extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -45,6 +47,13 @@ class UserSession extends Model
|
|||||||
*/
|
*/
|
||||||
public $client_ip = '';
|
public $client_ip = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标识
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $deleted = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 过期时间
|
* 过期时间
|
||||||
*
|
*
|
||||||
@ -71,6 +80,18 @@ class UserSession extends Model
|
|||||||
return 'kg_user_session';
|
return 'kg_user_session';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
parent::initialize();
|
||||||
|
|
||||||
|
$this->addBehavior(
|
||||||
|
new SoftDelete([
|
||||||
|
'field' => 'deleted',
|
||||||
|
'value' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function beforeCreate()
|
public function beforeCreate()
|
||||||
{
|
{
|
||||||
$this->create_time = time();
|
$this->create_time = time();
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||||
|
|
||||||
class UserToken extends Model
|
class UserToken extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -45,6 +47,13 @@ class UserToken extends Model
|
|||||||
*/
|
*/
|
||||||
public $client_ip = '';
|
public $client_ip = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标识
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $deleted = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 过期时间
|
* 过期时间
|
||||||
*
|
*
|
||||||
@ -71,6 +80,18 @@ class UserToken extends Model
|
|||||||
return 'kg_user_token';
|
return 'kg_user_token';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
parent::initialize();
|
||||||
|
|
||||||
|
$this->addBehavior(
|
||||||
|
new SoftDelete([
|
||||||
|
'field' => 'deleted',
|
||||||
|
'value' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function beforeCreate()
|
public function beforeCreate()
|
||||||
{
|
{
|
||||||
$this->create_time = time();
|
$this->create_time = time();
|
||||||
|
@ -18,10 +18,26 @@ class UserSession extends Repository
|
|||||||
* @param int $userId
|
* @param int $userId
|
||||||
* @return ResultsetInterface|Resultset|UserSessionModel[]
|
* @return ResultsetInterface|Resultset|UserSessionModel[]
|
||||||
*/
|
*/
|
||||||
public function findByUserId($userId)
|
public function findUserActiveSessions($userId)
|
||||||
{
|
{
|
||||||
return UserSessionModel::query()
|
return UserSessionModel::query()
|
||||||
->where('user_id = :user_id:', ['user_id' => $userId])
|
->where('user_id = :user_id:', ['user_id' => $userId])
|
||||||
|
->andWhere('deleted = 0')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $userId
|
||||||
|
* @param int $minutes
|
||||||
|
* @return ResultsetInterface|Resultset|UserSessionModel[]
|
||||||
|
*/
|
||||||
|
public function findUserRecentSessions($userId, $minutes = 10)
|
||||||
|
{
|
||||||
|
$createTime = time() - $minutes * 60;
|
||||||
|
|
||||||
|
return UserSessionModel::query()
|
||||||
|
->where('user_id = :user_id:', ['user_id' => $userId])
|
||||||
|
->andWhere('create_time > :create_time:', ['create_time' => $createTime])
|
||||||
->execute();
|
->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,26 @@ class UserToken extends Repository
|
|||||||
* @param int $userId
|
* @param int $userId
|
||||||
* @return ResultsetInterface|Resultset|UserTokenModel[]
|
* @return ResultsetInterface|Resultset|UserTokenModel[]
|
||||||
*/
|
*/
|
||||||
public function findByUserId($userId)
|
public function findUserActiveTokens($userId)
|
||||||
{
|
{
|
||||||
return UserTokenModel::query()
|
return UserTokenModel::query()
|
||||||
->where('user_id = :user_id:', ['user_id' => $userId])
|
->where('user_id = :user_id:', ['user_id' => $userId])
|
||||||
->execute();
|
->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $userId
|
||||||
|
* @param int $minutes
|
||||||
|
* @return ResultsetInterface|Resultset|UserTokenModel[]
|
||||||
|
*/
|
||||||
|
public function findUserRecentTokens($userId, $minutes = 10)
|
||||||
|
{
|
||||||
|
$createTime = time() - $minutes * 60;
|
||||||
|
|
||||||
|
return UserTokenModel::query()
|
||||||
|
->where('user_id = :user_id:', ['user_id' => $userId])
|
||||||
|
->andWhere('create_time > :create_time:', ['create_time' => $createTime])
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ class Api extends AuthService
|
|||||||
{
|
{
|
||||||
$repo = new UserTokenRepo();
|
$repo = new UserTokenRepo();
|
||||||
|
|
||||||
$records = $repo->findByUserId($userId);
|
$records = $repo->findUserActiveTokens($userId);
|
||||||
|
|
||||||
if ($records->count() == 0) return;
|
if ($records->count() == 0) return;
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class Home extends AuthService
|
|||||||
|
|
||||||
$repo = new UserSessionRepo();
|
$repo = new UserSessionRepo();
|
||||||
|
|
||||||
$records = $repo->findByUserId($userId);
|
$records = $repo->findUserActiveSessions($userId);
|
||||||
|
|
||||||
if ($records->count() == 0) return;
|
if ($records->count() == 0) return;
|
||||||
|
|
||||||
|
@ -12,13 +12,19 @@ use App\Exceptions\Forbidden as ForbiddenException;
|
|||||||
use App\Library\Utils\Password as PasswordUtil;
|
use App\Library\Utils\Password as PasswordUtil;
|
||||||
use App\Library\Validators\Common as CommonValidator;
|
use App\Library\Validators\Common as CommonValidator;
|
||||||
use App\Models\Account as AccountModel;
|
use App\Models\Account as AccountModel;
|
||||||
|
use App\Models\Client as ClientModel;
|
||||||
use App\Models\User as UserModel;
|
use App\Models\User as UserModel;
|
||||||
use App\Repos\Account as AccountRepo;
|
use App\Repos\Account as AccountRepo;
|
||||||
use App\Repos\User as UserRepo;
|
use App\Repos\User as UserRepo;
|
||||||
|
use App\Repos\UserSession as UserSessionRepo;
|
||||||
|
use App\Repos\UserToken as UserTokenRepo;
|
||||||
|
use App\Traits\Client as ClientTrait;
|
||||||
|
|
||||||
class Account extends Validator
|
class Account extends Validator
|
||||||
{
|
{
|
||||||
|
|
||||||
|
use ClientTrait;
|
||||||
|
|
||||||
public function checkAccount($name)
|
public function checkAccount($name)
|
||||||
{
|
{
|
||||||
$account = null;
|
$account = null;
|
||||||
@ -175,6 +181,34 @@ class Account extends Validator
|
|||||||
if ($locked && !$expired) {
|
if ($locked && !$expired) {
|
||||||
throw new ForbiddenException('account.locked');
|
throw new ForbiddenException('account.locked');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->checkFloodLogin($user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkFloodLogin($userId)
|
||||||
|
{
|
||||||
|
$clientIp = $this->getClientIp();
|
||||||
|
$clientType = $this->getClientType();
|
||||||
|
|
||||||
|
if ($clientType == ClientModel::TYPE_PC) {
|
||||||
|
$repo = new UserSessionRepo();
|
||||||
|
$records = $repo->findUserRecentSessions($userId, 10);
|
||||||
|
} else {
|
||||||
|
$repo = new UserTokenRepo();
|
||||||
|
$records = $repo->findUserRecentTokens($userId, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($records->count() == 0) return;
|
||||||
|
|
||||||
|
$clientIps = array_column($records->toArray(), 'client_ip');
|
||||||
|
|
||||||
|
$countValues = array_count_values($clientIps);
|
||||||
|
|
||||||
|
foreach ($countValues as $ip => $count) {
|
||||||
|
if ($clientIp == $ip && $count > 4) {
|
||||||
|
throw new ForbiddenException('account.flood_login');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ $error['captcha.invalid_code'] = '无效的验证码';
|
|||||||
*/
|
*/
|
||||||
$error['account.not_found'] = '账号不存在';
|
$error['account.not_found'] = '账号不存在';
|
||||||
$error['account.locked'] = '账号被锁定,无法登录';
|
$error['account.locked'] = '账号被锁定,无法登录';
|
||||||
|
$error['account.flood_login'] = '帐号泛滥登录';
|
||||||
$error['account.login_pwd_incorrect'] = '登录密码不正确';
|
$error['account.login_pwd_incorrect'] = '登录密码不正确';
|
||||||
$error['account.invalid_login_name'] = '无效的登录名';
|
$error['account.invalid_login_name'] = '无效的登录名';
|
||||||
$error['account.invalid_email'] = '无效的电子邮箱';
|
$error['account.invalid_email'] = '无效的电子邮箱';
|
||||||
|
46
db/migrations/20211019093522.php
Normal file
46
db/migrations/20211019093522.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||||
|
* @license https://opensource.org/licenses/GPL-2.0
|
||||||
|
* @link https://www.koogua.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Phinx\Db\Adapter\MysqlAdapter;
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class V20211019093522 extends AbstractMigration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$this->alterUserSessionTable();
|
||||||
|
$this->alterUserTokenTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function alterUserSessionTable()
|
||||||
|
{
|
||||||
|
$this->table('kg_user_session')
|
||||||
|
->addColumn('deleted', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '0',
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'signed' => false,
|
||||||
|
'comment' => '删除标识',
|
||||||
|
'after' => 'client_ip',
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function alterUserTokenTable()
|
||||||
|
{
|
||||||
|
$this->table('kg_user_token')
|
||||||
|
->addColumn('deleted', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '0',
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'signed' => false,
|
||||||
|
'comment' => '删除标识',
|
||||||
|
'after' => 'client_ip',
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user