diff --git a/app/Http/Home/Views/article/show.volt b/app/Http/Home/Views/article/show.volt index 62fa935f..9941b176 100644 --- a/app/Http/Home/Views/article/show.volt +++ b/app/Http/Home/Views/article/show.volt @@ -97,7 +97,7 @@ - {% 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}) %}
diff --git a/app/Http/Home/Views/chapter/live/active.volt b/app/Http/Home/Views/chapter/live/active.volt index 82cd87ee..663240e9 100644 --- a/app/Http/Home/Views/chapter/live/active.volt +++ b/app/Http/Home/Views/chapter/live/active.volt @@ -57,7 +57,7 @@
- {% 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}) %}
diff --git a/app/Http/Home/Views/chapter/read.volt b/app/Http/Home/Views/chapter/read.volt index 746a7bb0..0813b106 100644 --- a/app/Http/Home/Views/chapter/read.volt +++ b/app/Http/Home/Views/chapter/read.volt @@ -43,7 +43,7 @@
- {% 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}) %}
diff --git a/app/Http/Home/Views/chapter/vod.volt b/app/Http/Home/Views/chapter/vod.volt index 5c5953f1..1b054626 100644 --- a/app/Http/Home/Views/chapter/vod.volt +++ b/app/Http/Home/Views/chapter/vod.volt @@ -43,7 +43,7 @@
- {% 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}) %}
diff --git a/app/Http/Home/Views/course/show.volt b/app/Http/Home/Views/course/show.volt index 5f6ee021..a9472cb7 100644 --- a/app/Http/Home/Views/course/show.volt +++ b/app/Http/Home/Views/course/show.volt @@ -94,7 +94,7 @@
- {% 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}) %}
diff --git a/app/Http/Home/Views/question/show.volt b/app/Http/Home/Views/question/show.volt index e7d9f36b..d8b3bbc1 100644 --- a/app/Http/Home/Views/question/show.volt +++ b/app/Http/Home/Views/question/show.volt @@ -97,7 +97,7 @@
- {% 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}) %}
diff --git a/app/Http/Home/Views/user/show.volt b/app/Http/Home/Views/user/show.volt index f39aa5ff..599caf83 100644 --- a/app/Http/Home/Views/user/show.volt +++ b/app/Http/Home/Views/user/show.volt @@ -96,7 +96,7 @@
- {% 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}) %}
diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php index fed579d7..184af632 100644 --- a/app/Library/AppInfo.php +++ b/app/Library/AppInfo.php @@ -16,7 +16,7 @@ class AppInfo protected $link = 'https://koogua.com'; - protected $version = '1.4.6'; + protected $version = '1.4.7'; public function __get($name) { diff --git a/app/Models/UserSession.php b/app/Models/UserSession.php index 5b6074a5..7b55481d 100644 --- a/app/Models/UserSession.php +++ b/app/Models/UserSession.php @@ -7,6 +7,8 @@ namespace App\Models; +use Phalcon\Mvc\Model\Behavior\SoftDelete; + class UserSession extends Model { @@ -45,6 +47,13 @@ class UserSession extends Model */ public $client_ip = ''; + /** + * 删除标识 + * + * @var int + */ + public $deleted = 0; + /** * 过期时间 * @@ -71,6 +80,18 @@ class UserSession extends Model return 'kg_user_session'; } + public function initialize() + { + parent::initialize(); + + $this->addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + public function beforeCreate() { $this->create_time = time(); diff --git a/app/Models/UserToken.php b/app/Models/UserToken.php index aad991e6..5ddda172 100644 --- a/app/Models/UserToken.php +++ b/app/Models/UserToken.php @@ -7,6 +7,8 @@ namespace App\Models; +use Phalcon\Mvc\Model\Behavior\SoftDelete; + class UserToken extends Model { @@ -45,6 +47,13 @@ class UserToken extends Model */ public $client_ip = ''; + /** + * 删除标识 + * + * @var int + */ + public $deleted = 0; + /** * 过期时间 * @@ -71,6 +80,18 @@ class UserToken extends Model return 'kg_user_token'; } + public function initialize() + { + parent::initialize(); + + $this->addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + public function beforeCreate() { $this->create_time = time(); diff --git a/app/Repos/UserSession.php b/app/Repos/UserSession.php index 2ea25a8d..26f1b542 100644 --- a/app/Repos/UserSession.php +++ b/app/Repos/UserSession.php @@ -18,10 +18,26 @@ class UserSession extends Repository * @param int $userId * @return ResultsetInterface|Resultset|UserSessionModel[] */ - public function findByUserId($userId) + public function findUserActiveSessions($userId) { return UserSessionModel::query() ->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(); } diff --git a/app/Repos/UserToken.php b/app/Repos/UserToken.php index d6730027..056d8358 100644 --- a/app/Repos/UserToken.php +++ b/app/Repos/UserToken.php @@ -18,11 +18,26 @@ class UserToken extends Repository * @param int $userId * @return ResultsetInterface|Resultset|UserTokenModel[] */ - public function findByUserId($userId) + public function findUserActiveTokens($userId) { return UserTokenModel::query() ->where('user_id = :user_id:', ['user_id' => $userId]) ->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(); + } + } diff --git a/app/Services/Auth/Api.php b/app/Services/Auth/Api.php index a29feb63..bd1534a5 100644 --- a/app/Services/Auth/Api.php +++ b/app/Services/Auth/Api.php @@ -79,7 +79,7 @@ class Api extends AuthService { $repo = new UserTokenRepo(); - $records = $repo->findByUserId($userId); + $records = $repo->findUserActiveTokens($userId); if ($records->count() == 0) return; diff --git a/app/Services/Auth/Home.php b/app/Services/Auth/Home.php index b5ab29d8..455c94a3 100644 --- a/app/Services/Auth/Home.php +++ b/app/Services/Auth/Home.php @@ -68,7 +68,7 @@ class Home extends AuthService $repo = new UserSessionRepo(); - $records = $repo->findByUserId($userId); + $records = $repo->findUserActiveSessions($userId); if ($records->count() == 0) return; diff --git a/app/Validators/Account.php b/app/Validators/Account.php index 095bcdfa..91a7b9de 100644 --- a/app/Validators/Account.php +++ b/app/Validators/Account.php @@ -12,13 +12,19 @@ use App\Exceptions\Forbidden as ForbiddenException; use App\Library\Utils\Password as PasswordUtil; use App\Library\Validators\Common as CommonValidator; use App\Models\Account as AccountModel; +use App\Models\Client as ClientModel; use App\Models\User as UserModel; use App\Repos\Account as AccountRepo; 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 { + use ClientTrait; + public function checkAccount($name) { $account = null; @@ -175,6 +181,34 @@ class Account extends Validator if ($locked && !$expired) { 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'); + } + } } } diff --git a/config/errors.php b/config/errors.php index 53a22bf1..35aa2658 100644 --- a/config/errors.php +++ b/config/errors.php @@ -47,6 +47,7 @@ $error['captcha.invalid_code'] = '无效的验证码'; */ $error['account.not_found'] = '账号不存在'; $error['account.locked'] = '账号被锁定,无法登录'; +$error['account.flood_login'] = '帐号泛滥登录'; $error['account.login_pwd_incorrect'] = '登录密码不正确'; $error['account.invalid_login_name'] = '无效的登录名'; $error['account.invalid_email'] = '无效的电子邮箱'; diff --git a/db/migrations/20211019093522.php b/db/migrations/20211019093522.php new file mode 100644 index 00000000..5946d094 --- /dev/null +++ b/db/migrations/20211019093522.php @@ -0,0 +1,46 @@ +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(); + } + +}