-
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;
+ }
}
]]
});