频率限制
为了防止滥用,你应该考虑为API添加频率限制。例如,你希望每一个用户在一分钟的时间内,最多调用五次API。如果调用过多,将会返回状态码429(太多的请求)。
准备
重复创建一个REST服务器小节中准备和如何做…的所有步骤。
- 创建一个migration,用于创建一个用户允许表,命令如下:
./yii migrate/create create_user_allowance_table
- 然后使用如下代码更新刚刚创建的migration,
up:
public function up(){$tableOptions = null;if ($this->db->driverName === 'mysql') {$tableOptions = 'CHARACTER SET utf8 COLLATEutf8_general_ci ENGINE=InnoDB';}$this->createTable('{{%user_allowance}}', ['user_id' => $this->primaryKey(),'allowed_number_requests' => $this->integer(10)->notNull(),'last_check_time' => $this->integer(10)->notNull()], $tableOptions);}
- 使用如下代码更新
down方法:
public function down(){$this->dropTable('{{%user_allowance}}');}
- 运行创建的
create_firm_tablemigration。 - 使用Gii模块生成
UserAllowance模型。
如何做…
首先,你必须更新@app/controllers/FilmController.php:
<?phpnamespace app\controllers;use yii\rest\ActiveController;use yii\filters\RateLimiter;use yii\filters\auth\QueryParamAuth;class FilmController extends ActiveController{public $modelClass = 'app\models\Film';public function behaviors(){$behaviors = parent::behaviors();$behaviors['authenticator'] = ['class' => QueryParamAuth::className(),];$behaviors['rateLimiter'] = ['class' => RateLimiter::className(),'enableRateLimitHeaders' => true];return $behaviors;}}
为了激活频率限制,User模型类应该实现yii\filters\RateLimitInterface,并实现三个方法:getRateLimit()、loadAllowance()、saveAllowance()。你需要添加RATE_LIMIT_NUMBER和RATE_LIMIT_RESET两个常数:
<?phpnamespace app\models;class User extends \yii\base\Object implements \yii\web\IdentityInterface, \yii\filters\RateLimitInterface{public $id;public $username;public $password;public $authKey;public $accessToken;const RATE_LIMIT_NUMBER = 5;const RATE_LIMIT_RESET = 60;// it means that user allowed only 5 requests per one minutepublic function getRateLimit($request, $action){return [self::RATE_LIMIT_NUMBER,self::RATE_LIMIT_RESET];}public function loadAllowance($request, $action){$userAllowance = UserAllowance::findOne($this->id);return $userAllowance ?[$userAllowance->allowed_number_requests,$userAllowance->last_check_time]:$this->getRateLimit($request, $action);}public function saveAllowance($request, $action,$allowance,$timestamp){$userAllowance = ($allowanceModel =UserAllowance::findOne($this->id)) ?$allowanceModel : new UserAllowance();$userAllowance->user_id = $this->id;$userAllowance->last_check_time = $timestamp;$userAllowance->allowed_number_requests =$allowance;$userAllowance->save();}// other User model methods}
工作原理…
一旦验证类实现了需要的接口,Yii将会自动使用[[yii\rest\Controller]]动作过滤器中配置的[[\yii\filter\RateLimiter]]来运行频率检查。我们也需要为authenticator行为添加QueryParamAuth类。所以,我们现在能够只需要通过查询参数传递一个access token来进行身份校验。你可以添加官方文档中RESTful web服务部分中描述的任何一个身份验证方法。
下面来解释我们的方法,非常容易理解。
getRateLimit():返回允许请求的最大数量和时间段(例如,[100,600]表示600秒内最多运行100次API调用)
loadAllowance():返回剩余运行请求的数量,以及上次检查频率限制的UNIX时间戳。
saveAllowance():保存剩余运行请求数量的值,以及当前UNIX时间戳。
我们将数据存放在MySQL数据库中。考虑到性能,你也许可以使用一个NoSQL数据库或者其它能更快获取和加载数据的存储系统。
现在我们来检查频率限制特性。运行如下代码:
curl -i "http://yii-book.app/films?access-token=100-token"
你将会得到如下输出:
HTTP/1.1 200 OKDate: Thu, 24 Sep 2015 01:35:51 GMTServer: ApacheX-Powered-By: PHP/5.5.23Set-Cookie: PHPSESSID=495a928978cc732bee853b83f521eba2; path=/;HttpOnlyExpires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidate, post-check=0,pre-check=0Pragma: no-cacheX-Rate-Limit-Limit: 5X-Rate-Limit-Remaining: 4X-Rate-Limit-Reset: 0X-Pagination-Total-Count: 5X-Pagination-Page-Count: 1X-Pagination-Current-Page: 1X-Pagination-Per-Page: 20Link: <http://yii-book.app/films?access-token=100-token&page=1>;rel=selfContent-Length: 301Content-Type: application/json; charset=UTF-8[{"id":1,"title":"Interstellar","release_year":2014},{"id":2,"title":"Harry Potter and the Philosopher'sStone","release_year":2001},{"id":3,"title":"Back to theFuture","release_year":1985},{"id":4,"title":"BladeRunner","release_year":1982},{"id":5,"title":"Dallas BuyersClub","release_year":2013}]
来看下返回的头部。当激活了频率限制,默认情况下每次响应将会发送当前频率限制的信息:
X-Rate-Limit-Limit:在一段时间内最大允许请求的数量 X-Rate-Limit-Remaining:在当前时间内容剩余的允许请求数量 X-Rate-Limit-Reset:这是需要等待的时间数,来获取最大数量的允许请求
现在尝试超过限制,在一分钟内运行超过5次,将会得到TooManyRequestsHttpExeption:
HTTP/1.1 429 Too Many RequestsDate: Thu, 24 Sep 2015 01:37:24 GMTServer: ApacheX-Powered-By: PHP/5.5.23Set-Cookie: PHPSESSID=bb630ca8a641ef92bd210c0a936e3149; path=/;HttpOnlyExpires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidate, post-check=0,pre-check=0Pragma: no-cacheX-Rate-Limit-Limit: 5X-Rate-Limit-Remaining: 0X-Rate-Limit-Reset: 60Content-Length: 131Content-Type: application/json; charset=UTF-8{"name":"Too Many Requests","message":"Rate limitexceeded.","code":0,"status":429,"type":"yii\\web\\TooManyRequestsHttpException"}
参考
欲了解更多信息,参考如下地址:
