1
0
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:
koogua 2021-10-19 20:23:34 +08:00
commit c2c1c9987e
17 changed files with 166 additions and 12 deletions

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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)
{ {

View File

@ -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();

View File

@ -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();

View File

@ -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();
} }

View File

@ -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();
}
} }

View File

@ -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;

View File

@ -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;

View File

@ -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');
}
}
} }
} }

View File

@ -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'] = '无效的电子邮箱';

View 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();
}
}