From c7b2fdac66d7d0ffc76f47841ededb1d8ba11b9a Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Thu, 9 Apr 2020 18:44:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=99=90=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Views/config/site.volt | 8 +-- app/Http/Api/Controllers/Controller.php | 14 +++- app/Http/Api/Controllers/PublicController.php | 23 +++++++ app/Http/Html5/Controllers/Controller.php | 14 +++- .../Html5/Controllers/PublicController.php | 23 +++++++ app/Http/Web/Controllers/Controller.php | 8 +++ app/Http/Web/Controllers/PublicController.php | 12 ++++ app/Models/AccessToken.php | 2 +- app/Models/RefreshToken.php | 2 +- app/Services/Auth/Api.php | 6 +- app/Services/Frontend/Service.php | 54 +-------------- app/Services/Service.php | 3 + app/Services/Throttle.php | 65 +++++++++++++++++++ app/Traits/Auth.php | 15 +++++ app/Traits/Security.php | 8 +++ config/config.default.php | 27 +++++++- public/static/admin/css/style.css | 6 ++ public/static/admin/js/xm-course.js | 32 +++++---- 18 files changed, 244 insertions(+), 78 deletions(-) create mode 100644 app/Http/Api/Controllers/PublicController.php create mode 100644 app/Http/Html5/Controllers/PublicController.php create mode 100644 app/Services/Throttle.php diff --git a/app/Http/Admin/Views/config/site.volt b/app/Http/Admin/Views/config/site.volt index c69c0777..dabcae6c 100644 --- a/app/Http/Admin/Views/config/site.volt +++ b/app/Http/Admin/Views/config/site.volt @@ -35,13 +35,13 @@
-
+
-
+
@@ -50,13 +50,13 @@
-
+
-
+
diff --git a/app/Http/Api/Controllers/Controller.php b/app/Http/Api/Controllers/Controller.php index 51a59722..cf0197b0 100644 --- a/app/Http/Api/Controllers/Controller.php +++ b/app/Http/Api/Controllers/Controller.php @@ -3,15 +3,23 @@ namespace App\Http\Api\Controllers; use App\Traits\Response as ResponseTrait; +use App\Traits\Security as SecurityTrait; +use Phalcon\Mvc\Dispatcher; class Controller extends \Phalcon\Mvc\Controller { - use ResponseTrait; + use ResponseTrait, SecurityTrait; - public function initialize() + public function beforeExecuteRoute(Dispatcher $dispatcher) { - + if (!$this->checkRateLimit()) { + $dispatcher->forward([ + 'controller' => 'public', + 'action' => 'throttle', + ]); + return false; + } } } diff --git a/app/Http/Api/Controllers/PublicController.php b/app/Http/Api/Controllers/PublicController.php new file mode 100644 index 00000000..0383d387 --- /dev/null +++ b/app/Http/Api/Controllers/PublicController.php @@ -0,0 +1,23 @@ +jsonError(['msg' => '请求过于频繁']); + } + +} diff --git a/app/Http/Html5/Controllers/Controller.php b/app/Http/Html5/Controllers/Controller.php index f8722e24..a7d35aba 100644 --- a/app/Http/Html5/Controllers/Controller.php +++ b/app/Http/Html5/Controllers/Controller.php @@ -3,15 +3,23 @@ namespace App\Http\Html5\Controllers; use App\Traits\Response as ResponseTrait; +use App\Traits\Security as SecurityTrait; +use Phalcon\Mvc\Dispatcher; class Controller extends \Phalcon\Mvc\Controller { - use ResponseTrait; + use ResponseTrait, SecurityTrait; - public function initialize() + public function beforeExecuteRoute(Dispatcher $dispatcher) { - + if (!$this->checkRateLimit()) { + $dispatcher->forward([ + 'controller' => 'public', + 'action' => 'throttle', + ]); + return false; + } } } diff --git a/app/Http/Html5/Controllers/PublicController.php b/app/Http/Html5/Controllers/PublicController.php new file mode 100644 index 00000000..6aaf6fa2 --- /dev/null +++ b/app/Http/Html5/Controllers/PublicController.php @@ -0,0 +1,23 @@ +jsonError(['msg' => '请求过于频繁']); + } + +} diff --git a/app/Http/Web/Controllers/Controller.php b/app/Http/Web/Controllers/Controller.php index 8dc90d38..f8af2b2e 100644 --- a/app/Http/Web/Controllers/Controller.php +++ b/app/Http/Web/Controllers/Controller.php @@ -20,6 +20,14 @@ class Controller extends \Phalcon\Mvc\Controller public function beforeExecuteRoute(Dispatcher $dispatcher) { + if (!$this->checkRateLimit()) { + $dispatcher->forward([ + 'controller' => 'public', + 'action' => 'throttle', + ]); + return false; + } + if ($this->isNotSafeRequest()) { if (!$this->checkHttpReferer() || !$this->checkCsrfToken()) { $dispatcher->forward([ diff --git a/app/Http/Web/Controllers/PublicController.php b/app/Http/Web/Controllers/PublicController.php index 8949721d..60773bf9 100644 --- a/app/Http/Web/Controllers/PublicController.php +++ b/app/Http/Web/Controllers/PublicController.php @@ -50,6 +50,18 @@ class PublicController extends \Phalcon\Mvc\Controller } } + /** + * @Get("/throttle", name="web.throttle") + */ + public function throttleAction() + { + $isAjaxRequest = is_ajax_request(); + + if ($isAjaxRequest) { + return $this->jsonError(['msg' => 'web请求过于频繁']); + } + } + /** * @Get("/content/img/{id:[0-9]+}", name="web.content.img") */ diff --git a/app/Models/AccessToken.php b/app/Models/AccessToken.php index 81bd7cd3..eadd87f6 100644 --- a/app/Models/AccessToken.php +++ b/app/Models/AccessToken.php @@ -55,7 +55,7 @@ class AccessToken extends Model public function beforeCreate() { $this->id = $this->getRandId($this->user_id); - $this->expiry_time = strtotime('+2 hours'); + $this->create_time = time(); } diff --git a/app/Models/RefreshToken.php b/app/Models/RefreshToken.php index f1c606bc..56340449 100644 --- a/app/Models/RefreshToken.php +++ b/app/Models/RefreshToken.php @@ -55,7 +55,7 @@ class RefreshToken extends Model public function beforeCreate() { $this->id = $this->getRandId($this->user_id); - $this->expiry_time = strtotime('+30 days'); + $this->create_time = time(); } diff --git a/app/Services/Auth/Api.php b/app/Services/Auth/Api.php index 94807194..eb4d4a2c 100644 --- a/app/Services/Auth/Api.php +++ b/app/Services/Auth/Api.php @@ -14,12 +14,16 @@ class Api extends AuthService public function saveAuthInfo(UserModel $user) { + $config = $this->getDI()->get('config'); + $accessToken = new AccessTokenModel(); $accessToken->user_id = $user->id; + $accessToken->expiry_time = time() + $config->access_token->lifetime; $accessToken->create(); $refreshToken = new RefreshTokenModel(); $refreshToken->user_id = $user->id; + $refreshToken->expiry_time = time() + $config->refresh_token->lifetime; $refreshToken->create(); $authInfo = [ @@ -31,7 +35,7 @@ class Api extends AuthService $key = $this->getCacheKey($accessToken->id); - $cache->save($key, $authInfo, 2 * 3600); + $cache->save($key, $authInfo, $config->access_token->lifetime); return new Collection([ 'access_token' => $accessToken->id, diff --git a/app/Services/Frontend/Service.php b/app/Services/Frontend/Service.php index 64000c70..afe1a4d7 100644 --- a/app/Services/Frontend/Service.php +++ b/app/Services/Frontend/Service.php @@ -2,59 +2,11 @@ namespace App\Services\Frontend; -use App\Models\User as UserModel; -use App\Repos\User as UserRepo; -use App\Services\Auth as AuthService; -use App\Validators\Validator as AppValidator; +use App\Traits\Auth as AuthTrait; use Phalcon\Mvc\User\Component; class Service extends Component { - public function getCurrentUser() - { - $authUser = $this->getAuthUser(); - - if (!$authUser) { - return $this->getGuestUser(); - } - - $userRepo = new UserRepo(); - - return $userRepo->findById($authUser['id']); - } - - public function getLoginUser() - { - $authUser = $this->getAuthUser(); - - $validator = new AppValidator(); - - $validator->checkAuthUser($authUser); - - $userRepo = new UserRepo(); - - return $userRepo->findById($authUser['id']); - } - - public function getAuthUser() - { - /** - * @var AuthService $auth - */ - $auth = $this->getDI()->get('auth'); - - return $auth->getAuthInfo(); - } - - public function getGuestUser() - { - $user = new UserModel(); - - $user->id = 0; - $user->name = 'guest'; - - return $user; - } - -} + use AuthTrait; +} \ No newline at end of file diff --git a/app/Services/Service.php b/app/Services/Service.php index 6b6305cb..6a999a7f 100644 --- a/app/Services/Service.php +++ b/app/Services/Service.php @@ -4,12 +4,15 @@ namespace App\Services; use App\Caches\SectionConfig as SectionConfigCache; use App\Library\Logger as AppLogger; +use App\Traits\Auth as AuthTrait; use Phalcon\Logger\Adapter\File as FileLogger; use Phalcon\Mvc\User\Component; class Service extends Component { + use AuthTrait; + /** * 获取Logger * diff --git a/app/Services/Throttle.php b/app/Services/Throttle.php new file mode 100644 index 00000000..68521a9c --- /dev/null +++ b/app/Services/Throttle.php @@ -0,0 +1,65 @@ +getDI()->get('config'); + + if ($config->throttle->enabled == false) { + return true; + } + + /** + * @var RedisCache $cache + */ + $cache = $this->getDI()->get('cache'); + + $sign = $this->getRequestSignature(); + + $cacheKey = $this->getCacheKey($sign); + + $rateLimit = $cache->get($cacheKey); + + if ($rateLimit) { + if ($rateLimit >= $config->throttle->rate_limit) { + return false; + } else { + $cache->increment($cacheKey, 1); + } + } else { + $cache->save($cacheKey, 1, $config->throttle->lifetime); + } + + return true; + } + + protected function getRequestSignature() + { + $authUser = $this->getAuthUser(); + + if (!empty($authUser->id)) { + return md5($authUser->id); + } + + $httpHost = $this->request->getHttpHost(); + $clientAddress = $this->request->getClientAddress(); + + if ($httpHost && $clientAddress) { + return md5($httpHost . '|' . $clientAddress); + } + + throw new \RuntimeException('Unable to generate the request signature.'); + } + + protected function getCacheKey($sign) + { + return "throttle:{$sign}"; + } + +} diff --git a/app/Traits/Auth.php b/app/Traits/Auth.php index 35f71b6f..4dc80334 100644 --- a/app/Traits/Auth.php +++ b/app/Traits/Auth.php @@ -2,15 +2,20 @@ namespace App\Traits; +use App\Exceptions\Unauthorized as UnauthorizedException; use App\Models\User as UserModel; use App\Repos\User as UserRepo; use App\Services\Auth as AuthService; use App\Validators\Validator as AppValidator; use Phalcon\Di; +use Yansongda\Supports\Collection; trait Auth { + /** + * @return UserModel + */ public function getCurrentUser() { $authUser = $this->getAuthUser(); @@ -24,6 +29,10 @@ trait Auth return $userRepo->findById($authUser->id); } + /** + * @return UserModel + * @throws UnauthorizedException + */ public function getLoginUser() { $authUser = $this->getAuthUser(); @@ -37,6 +46,9 @@ trait Auth return $userRepo->findById($authUser->id); } + /** + * @return UserModel + */ public function getGuestUser() { $user = new UserModel(); @@ -47,6 +59,9 @@ trait Auth return $user; } + /** + * @return Collection|null + */ public function getAuthUser() { /** diff --git a/app/Traits/Security.php b/app/Traits/Security.php index fe5c1664..9c9709cb 100644 --- a/app/Traits/Security.php +++ b/app/Traits/Security.php @@ -2,6 +2,7 @@ namespace App\Traits; +use App\Services\Throttle; use Phalcon\Di; use Phalcon\Http\Request; @@ -38,6 +39,13 @@ trait Security return $httpHost == $request->getHttpHost(); } + public function checkRateLimit() + { + $throttle = new Throttle(); + + return $throttle->checkRateLimit(); + } + public function isNotSafeRequest() { /** diff --git a/config/config.default.php b/config/config.default.php index fab3457c..870109ad 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -85,13 +85,38 @@ $config['redis']['index'] = 0; /** * 缓存有效期(秒) */ -$config['redis']['lifetime'] = 24 * 3600; +$config['redis']['lifetime'] = 2 * 86400; /** * 会话有效期(秒) */ $config['session']['lifetime'] = 2 * 3600; +/** + * 访问令牌有效期(秒) + */ +$config['access_token']['lifetime'] = 2 * 3600; + +/** + * 刷新令牌有效期(秒) + */ +$config['refresh_token']['lifetime'] = 30 * 86400; + +/** + * 限流开启 + */ +$config['throttle']['enabled'] = true; + +/** + * 限流有效期(秒) + */ +$config['throttle']['lifetime'] = 60; + +/** + * 限流频率 + */ +$config['throttle']['rate_limit'] = 60; + /** * 日志级别 */ diff --git a/public/static/admin/css/style.css b/public/static/admin/css/style.css index c2a73e02..6b211633 100644 --- a/public/static/admin/css/style.css +++ b/public/static/admin/css/style.css @@ -28,6 +28,12 @@ width: 30px; } +.kg-input-inline { + float: left; + width: 250px; + margin-right: 10px; +} + .kg-btn-verify { color: green; } diff --git a/public/static/admin/js/xm-course.js b/public/static/admin/js/xm-course.js index 9750549f..3ddc43cb 100644 --- a/public/static/admin/js/xm-course.js +++ b/public/static/admin/js/xm-course.js @@ -35,44 +35,50 @@ function xmCourse(data, url) { table.render({ id: 'course-table', elem: '#course-table', + width: 900, url: url, page: true, cols: [[ - {field: 'id', title: '编号', width: 40}, - {field: 'title', title: '标题', width: 340}, + {field: 'id', title: '编号', width: 50}, + {field: 'title', title: '标题', width: 390}, { - field: 'model', title: '类型', width: 40, templet: function (d) { - if (d.model === 1) { + field: 'model', title: '类型', width: 50, templet: function (d) { + if (d.model === 'vod') { return '点播'; - } else if (d.model === 2) { + } else if (d.model === 'live') { return '直播'; - } else if (d.model === 3) { + } else if (d.model === 'read') { return '图文'; } } }, { - field: 'level', title: '难度', width: 40, templet: function (d) { - if (d.level === 1) { + field: 'level', title: '难度', width: 50, templet: function (d) { + if (d.level === 'entry') { return '入门'; - } else if (d.level === 2) { + } else if (d.level === 'junior') { return '初级'; - } else if (d.level === 3) { + } else if (d.level === 'medium') { return '中级'; - } else if (d.level === 4) { + } else if (d.level === 'senior') { return '高级'; } } }, { - field: 'user_count', title: '用户', width: 40, templet: function (d) { + field: 'user_count', title: '用户', width: 50, templet: function (d) { return d.user_count; } }, { - field: 'market_price', title: '价格', width: 40, templet: function (d) { + field: 'market_price', title: '市场价', width: 50, templet: function (d) { return '¥' + d.market_price; } + }, + { + field: 'vip_price', title: '会员价', width: 50, templet: function (d) { + return '¥' + d.vip_price; + } } ]] });