Yii2 restful api创建,认证授权以及速率控制
下面是对restful从创建到速率控制的一个详细流程介绍,里面的步骤以及截图尽可能详细,熟悉restful的盆友可能觉得过于繁琐,新手不妨耐心仔细看一下。
一.Api的创建
1.复制一个frontend或者backend,将其重命名为api放在同级目录下
2.然后删除controllers和views文件夹,然后将api文件中的frontend替换为api(比如命名空间,相关配置等),这点非常重要!!!
3.打开cmd命令行,cd进入项目所在根目录C:\wamp\www\advanced下,init 根据提示进行初始化设置,之后修改common\config\main-local.php文件中的数据库相关配置。这个比较简单。
4.需要修改common\config\bootstrap.php文件,对新建的应用增加alias别名,如下:
Yii::setAlias('@api', dirname(dirname(__DIR__)) . '/api');
5.为新建的api应用程序美化路由,为了后续方便访问,进行域名配置
1)首先保证你的web服务器开启rewrite规则,细节我们就不说了,不过这是前提。
2)给api目录配置一个访问地址:www.dsgn.com指向 api/web,不会的盆友请参考我的另一篇博客,关于Apache的域名配置
3)接着,在应用入口同级增加.htaccess文件就好,我们以apache为例
.htaccess文件内容:
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
RewriteRule \.svn\/ /404.html
RewriteRule \.git\/ /404.html
6.用了便于演示说明,我们新建一张数据表good表,字段自己随意设置,并向其中插入几条数据。
7.通过gii生成modules
注意:由于刚才配置的域名是访问到api/web,现在gii生成器的地址我用localhost去访问fronted/web/index.php
地址:http://localhost/advanced/frontend/web/index.php?r=gii
然后依次生成模块 控制器 模型
8.同时在 api/config/main.php 中做如下配置,这一步也很重要
1)添加module的配置
'modules' => [
'v1' => [
'class' => 'api\modules\v1\Module',
],
],
2)修改controllerNamespace
'controllerNamespace' => 'api\controllers',
3)开启urlManager,如下配置
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
//为Goods配置Url规则
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/good']],
],
],
4)删除
'errorHandler' => [
'errorAction' => 'site/error',
],
9. 重新配置控制器。为了实现restful风格的api,在yii2中,我们需要对 api\modules\v1\controllers下的GoodController控制器进行一下改写
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
class GoodController extends ActiveController
{
public $modelClass = 'api\models\Good';
}
说明:翻阅资料的时候,看到有人提出,在这个控制器里面自定义方法,但是访问的时候报404
原因如下:RESTful是以资源为中心而不是页面,所以YII2给每个资源只给了有限的几个action,而且是以Action类的形式写的,
它默认有create, delete, update, index, view等的一下方法
所创建的 API 包括:
GET /users: 逐页列出所有用户
POST /users: 创建一个新用户
GET /users/123: 返回用户 123 的详细信息
PATCH /users/123 and PUT /users/123: 更新用户123
DELETE /users/123: 删除用户123
1)框架默认的action都写在yii\rest包下(XXXAction.php就表示相应的动词操作,源代码很少,你可以直接看看),
2)如果这个资源的所有动词都是默认的话是则它的controller里除了modelClass是不用写任何代码的.
3)如果想要自定义动词必须继承rest包下对应的Action类,然后在controller重写actions指向你的新action,从而实现重写
打开文件api\modules\v1\controllers\UserController,参考代码如下:
如果我们想在自己定义不同的api接口的话?那么我们可以通过配置实现,在main.php的主文件中:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => ['v1/users'],
'pluralize' => false,
'extraPatterns' => [
'GET versions' => 'version',
'GET search/<id:\d+>' => 'search',
'POST newusers' => 'add'
],
],
],
],
"extraPatterns"这个属性是额外模式配置
a)'GET versions' => 'version',代表获取接口版本,例如/v1/users/versions ,对应的内联操作actionVersion();
b)'GET searches/<id:\d+>' => 'search', 代表搜索一个指定id的用户,例如/v1/users/searches/1,对应的内联操作actionSearches();
c)'POST newusers' => 'add',代表添加一个用户,例如/v1/users/newusers,对应的内联操作actionAdd();
我们只需要在控制器中实现这些方法,完成相应的逻辑业务,那么各个需要的接口就完成了。
9.模拟请求操作
到这一步就可以用postman来测试,不会安装或者调试的盆友请网上搜索一下,当然也可以用其他测试工具来调试。
从上面截图中可以清楚的看到,GET 方式请求http://www.dsgn.com/v1/goods, 已经能够很方便的获取我们表中的数据了。
补充说明:1)Yii 将在末端使用的控制器的名称自动变为复数。这个要重点注意!
2)这个地方使用之所以使用goods这个复数,是因为在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。
一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
3)当然,用yii2的restful写api,暂时可以不用框架推荐的yii\rest\UrlRule,就可以写出一般路由的api了,主要因为yii\rest\UrlRule的路由简略了很多,不利于直接识别api代表的意思。
例子:
使用yii\rest\UrlRule,访问链接为/profiles/3
使用一般URL规则'<controller:\w+>/<action:\w+>' => '<controller>/<action>', 访问链接为/profile/view?id=3
测试结果都一致。
httpx://api.yii.com/profile/view?id=1&expand=user
yii的restful要获得关联表数据必须要带一个expand参数表明需要使用的关联数据
4)如果是用restful自带的路由规则,要解决访问存在的复数问题的话,需要添加'pluralize' => false这个条件,具体如下:
'rules' => [
['class' => 'yii\rest\UrlRule',
'controller' => ['v1/good'],
'pluralize' => false
],
],
到此为止,用http://www.dsgn.com/v1/good就能成功测试访问了。
但是,有个问题出现了,用postman测试的数据,可以看到是json格式的,那是因为postman测试工具可以自动转化成json美化格式,
实际中我们用浏览器访问该url看到如下,(这跟是否用复数访问没关系)
解决方法是,在GoodController控制器下重写behaviors函数,具体做法是加这样一段代码:
public function behaviors()
{
$behaviors=parent::behaviors();
$behaviors['contentNegotiator']['formats'] = [
'application/json'=>Response::FORMAT_JSON
];
return $behaviors;
}
说明:Yii2 RESTful支持JSON和XML格式,如果想指定返回数据的格式,需要配置yii\filters\ContentNegotiator::formats属性
现在重新去访问http://www.dsgn.com/v1/good就可以看到以json格式解析出来了。O(∩_∩)O~~~
补充说明:
API的目录结构有2种方式:
之前的创建测试都是在方式1的情况下进行的,其实这两种方式的差异并不大,现在对方式2进行补充说明:
打开api\config\main.php中,有如下配置:
二、认证授权
1.需要设置认证的原因:和Web应用不同,RESTful APIs 通常是无状态的,也就意味着不应使用sessions 或 cookies,因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过sessions 或 cookies维护,
常用的做法是每个请求都发送一个秘密的access token来认证用户,由于access token可以唯一识别和认证用户, API 请求应通过HTTPS来防止man-in-the-middle (MitM) 中间人攻击.
2.准备工作:首先,要创建一个user表,可以用yii2自带的migrate创建,不会的请查阅相关资料。或者用sql语句建表,如下
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
`password_hash` varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
`password_reset_token` varchar(50) NOT NULL DEFAULT '' COMMENT '密码token',
`email` varchar(20) NOT NULL DEFAULT '' COMMENT '邮箱',
`auth_key` varchar(50) NOT NULL DEFAULT '',
`status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
`created_at` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`updated_at` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`access_token` varchar(50) NOT NULL DEFAULT '' COMMENT 'restful请求token',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `access-token` (`access-token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入一条或者几条数据,以便后期测试
3.配置user应用组件(不是必要的,但是推荐配置):
说明:1) 设置yii\web\User::enableSession属性为false(因为RESTful APIs为无状态的,当yii\web\User::enableSession为false,请求中的用户认证状态就不能通过session来保持)
2) 设置yii\web\User::loginUrl属性为null(显示一个HTTP 403 错误而不是跳转到登录界面)
3) 'identityClass' => 'api\models\User',
指定认证的model类,现在是在api\models\User这个类中,那么我们需要在api\models\User这个类中实现,yii\web\IdentityInterface这个类中的所有定义的接口方法
'user' => [
'identityClass' => 'api\models\User',
'enableAutoLogin' => true,
'enableSession'=>false,
'identityCookie' => ['name' => '_identity-api', 'httpOnly' => true],
],
4.为控制器配置authenticator行为指定认证方式
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
use yii\helpers\ArrayHelper;
use yii\filters\auth\QueryParamAuth;
class UserController extends ActiveController
{
public $modelClass = 'api\models\User';
public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => QueryParamAuth::className(),
];
$behaviors['contentNegotiator']['formats'] = [
'application/json'=> Response::FORMAT_JSON
];
return $behaviors;
}
}
说明:behaviors()这个函数的是定义这个控制器类的行为,也就是每一次访问这个控制器的方法,
都会执行这个behaviors中定义的各种行为,认证也是这个流程,我们访问一个api接口时,
就会执行yii\filters\auth\QueryParamAuth的这个文件的authenticate()这个方法
5.在api/config/main.php中
我们需要在配置文件中保证,步骤3配置user组件中曾提到过,这里作为补充说明。
'user' => [
'identityClass' => 'api\models\User',
],
这里是指定认证的model类,现在是在api\models\User这个类中,那么我们需要在api\models\User这个类中实现yii\web\IdentityInterface这个类中的所有定义的接口方法,在api/models/User.php中代码如下:
<?php
namespace api\models;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
class User extends ActiveRecord implements IdentityInterface{
public static function tableName()
{
return 'user';
}
public function rules()
{
return [
[['username', 'auth_key', 'password_hash', 'email', 'created_at', 'updated_at'], 'required'],
[['status', 'created_at', 'updated_at', 'allowance', 'allowance_updated_at'], 'integer'],
[['username', 'password_hash', 'password_reset_token', 'email'], 'string', 'max' => 255],
[['auth_key'], 'string', 'max' => 32],
[['access_token'], 'string', 'max' => 60],
[['username'], 'unique'],
[['email'], 'unique'],
[['password_reset_token'], 'unique'],
];
}
public function attributeLabels()
{
return [
'id' => 'ID',
'username' => 'Username',
'auth_key' => 'Auth Key',
'password_hash' => 'Password Hash',
'password_reset_token' => 'Password Reset Token',
'email' => 'Email',
'status' => 'Status',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
'access_token' => 'Access Token',
'allowance' => 'Allowance',
'allowance_updated_at' => 'Allowance Updated At',
];
}
//实现接口里的全部方法
public static function findIdentity($id)
{
return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
}
public static function findIdentityByAccessToken($token, $type = null)
{
return static::findOne(['access_token' => $token]); //数据库user表中的字段access_token
}
//这个就是我们进行yii\filters\auth\QueryParamAuth调用认证的函数,下面会说到。
public function loginByAccessToken($accessToken, $type) {
//查询数据库中有没有存在这个token
return static::findIdentityByAccessToken($accessToken, $type);
}
public static function findByUsername($username)
{
return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]);
}
public function getId()
{
return $this->getPrimaryKey();
}
public function getAuthKey()
{
return $this->auth_key;
}
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
这样一来,整个认证的过程就完成了。
现在对数据库user表中的access_token和访问时需携带的参数access-token进行简单说明:
1.user表中的参数access_token作用:
在实现IdentityInterface接口类,重写findIdentityByAccessToken()方法中,根据access_token校验用户身份,获取用户信息
2.url访问时,通过携带的参数access-token作用:
利用该参数进行认证。此参数其实是对yii\filters\auth\QueryParanAuth中的属性$tokenParam设置的值。
细心的盆友会发现,这其实是UserController中行为中配置authenticator行为对应的认证类。
$tokenParam这个属性是设置url附带的token的参数key,我们可以在behaviors()这个函数中配置修改:
更改之后url附带的参数key就应该是token.
4.如此一来,我们先通过postman模拟不带access-token请求看结果
提示401 我们没有权限访问!
5.我们在请求的链接上携带正确的access-token,认证通过后,控制器会再继续执行其他检查(频率限制、操作权限等),才可以返回正确的用户信息。
如果携带access-token在user表中找不到与该字段对应的值,那么结果跟步骤4中显示的还是一样。这里访问http://www.dsgn.com/v1/user?access-token=123456,进行测试:
如果此处要指定哪些字段放在结果中,可以使用fields或者expand参数,如:http://www.dsgn.com/v1/user?fields=id,username&access-token=123456,这样GET的结果就只显示包含id,username这2个字段的结果了
6.授权:在用户认证通过后,你可能想检查他是否有足够的权限来访问请求资源的这个动作, 那么就在api/modules/controllers下对应的控制器中重写函数checkAccess
public function checkAccess($action, $model = null, $params = [])
{
}
三、速率控制
说明:为防止滥用,可以增加速率限制。例如,限制每个用户的API的使用是在60秒内最多5次的API调用,
如果一个用户同一个时间段内太多的请求被接收,将返回响应状态代码 429 (这意味着过多的请求)。
1.在我们启用了速率限制后,Yii会自动使用yii\filters\RateLimiter为yii\rest\Controller配置一个行为过滤器来执行速率限制检查。
如果速度超出限制,该速率限制器将抛出一个yii\web\TooManyRequestsHttpException。
2.给user表添加2个字段
`allowance` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'restful剩余的允许的请求数',
`allowance_updated_at` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'restful请求的UNIX时间戳数',
3.在api相应的控制器下,behaviors函数下添加以下代码:
$behaviors['rateLimiter'] = [
'class' => RateLimiter::className(),
'enableRateLimitHeaders' => true,
];
3.在user表中使用两列来记录容差和时间戳信息。为了提高性能,可以考虑使用缓存或NoSQL存储这些信息。修改api/models/User.php,改动或者加入以下代码:
use yii\filters\RateLimitInterface;
class User extends ActiveRecord implements IdentityInterface,RateLimitInterface
//限制同一接口某时间段过多的请求,需要实现yii\filters\RateLimitInterface接口的全部方法====================
// 返回某一时间允许请求的最大数量,比如设置60秒内最多5次请求(小数量方便我们模拟测试)
public function getRateLimit($request, $action){
return [5, 60];
}
// 回剩余的允许的请求和相应的UNIX时间戳数 当最后一次速率限制检查时
public function loadAllowance($request, $action){
return [$this->allowance, $this->allowance_updated_at];
}
// 保存允许剩余的请求数和当前的UNIX时间戳
public function saveAllowance($request, $action, $allowance, $timestamp){
$this->allowance = $allowance;
$this->allowance_updated_at = $timestamp;
$this->save();
}
4.通过postman请求http://www.dsgn.com/v1/user?access-token=12345,通过测试结果发现,60秒内调用超过5次API接口,我们会得到状态为429太多请求的异常信息。
注意:以下红框里面的头信息内容,可以通过在api/models/User.php中,通过设置禁用掉
5.错误处理
Yii的REST框架的HTTP状态代码可参考如下:
200: OK。一切正常。
201: 响应 POST 请求时成功创建一个资源。Location header 包含的URL指向新创建的资源。
204: 该请求被成功处理,响应不包含正文内容 (类似 DELETE 请求)。
304: 资源没有被修改。可以使用缓存的版本。
400: 错误的请求。可能通过用户方面的多种原因引起的,例如在请求体内有无效的JSON 数据,无效的操作参数,等等。
401: 验证失败。
403: 已经经过身份验证的用户不允许访问指定的 API 末端。
404: 所请求的资源不存在。
405: 不被允许的方法。 请检查 Allow header 允许的HTTP方法。
415: 不支持的媒体类型。 所请求的内容类型或版本号是无效的。
422: 数据验证失败 (例如,响应一个 POST 请求)。 请检查响应体内详细的错误消息。
429: 请求过多。 由于限速请求被拒绝。
500: 内部服务器错误。 这可能是由于内部程序错误引起的。
Yii2 restful api创建,认证授权以及速率控制的更多相关文章
- Yii2 Restful api创建
-
yii2 RESTful API Develop
参考文档:http://www.yiiframework.com/doc-2.0/guide-rest.html 以 DB 中的 news 表为例创建该资源的 RESTful API,最终的测试通过工 ...
-
RESTful Api 身份认证安全性设计
REST是一种软件架构风格.RESTful Api 是基于 HTTP 协议的 Api,是无状态传输.它的核心是将所有的 Api 都理解为一个网络资源.将所有的客户端和服务器的状态转移(动作)封装到 H ...
-
关于RESTFUL API 安全认证方式的一些总结
常用认证方式 在之前的文章REST API 安全设计指南与使用 AngularJS & NodeJS 实现基于 token 的认证应用两篇文章中,[译]web权限验证方法说明中也详细介绍,一般 ...
-
RESTful Api 身份认证中的安全性设计探讨
REST 是一种软件架构风格.RESTful Api 是基于 HTTP 协议的 Api,是无状态传输.它的核心是将所有的 Api 都理解为一个网络资源.将所有的客户端和服务器的状态转移(动作)封装到 ...
-
关于 RESTFUL API 安全认证方式的一些总结
常用认证方式 在之前的文章REST API 安全设计指南与使用 AngularJS & NodeJS 实现基于 token 的认证应用两篇文章中,[译]web权限验证方法说明中也详细介绍,一般 ...
-
Yii2 Restful Api 401
采用Yii2 Restful Api方式为APP提供数据,默认你已经做好了所有的编码和配置工作.采用Postman测试接口: 出现这个画面的一个可能原因是:access_token的写法有误,如果你使 ...
-
yii2 RESTful API 405 Method Not Allowed
关于 Yii2 中 RESTful API 的开发,可以参考另一篇随笔 http://www.cnblogs.com/ganiks/p/yii2-restful-api-dev.html 测试的过程中 ...
-
Yii2 Restful api设计--App接口编程
Yii2框架写一套RESTful风格的API,对照魏曦教你学 一,入门 一.目录结构 实现一个简单地RESTful API只需用到三个文件.目录如下: frontend ├─ config │ └ m ...
随机推荐
- 简单好记的Jdk 环境变量配置
-
Qt model和tableview的使用
QT中的model和tableview都是采用index索引 index含有两个成员变量一个是row 一个是column 对应该索引的行号.列号 model提供数据 view提供视图 ...
-
从基础开始,从一个SQLHelper开始
最开始考虑的问题有这三点: 1.Access和SQLServer都要能用. 2.尽量简单,清晰. 3.性能不出大问题. public class SQLHelp { #region 私有域 priva ...
-
gitHub入门指导
Github可以托管各种git库,并提供一个web界面,但与其它像 SourceForge或Google Code这样的服务不同,GitHub的独特卖点在于从另外一个项目进行分支的简易性.为一个项目贡 ...
-
MyBatis学习总结_07_Mybatis缓存
一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...
-
使用jquery trigger 触发a标签的click事件取代window.open方法
var ohtml='<div class="friend-dialog tac pt15 pb20">'+ '<div class="f-h32&qu ...
-
安装4.x版本的express开发框架
错误版本(未更新前的安装方法,更新后就不是这么安装了,好多网上的教程还是这种方法,所以这里先写明了,旧版这里是可以通过的,但是新版4.x就不行了,请用分割线下边的方法) 本文演示在Linux上安装 ...
-
OPENCV基本滤波算法
图像滤波的主要目的是为了在保留图像细节的情况下尽量的对图像的噪声进行消除,从而是后来的图像处理变得更加的方便. 图像的滤波效果要满足两个条件:1.不能损坏图像的轮廓和边缘这些重要的特征信息.2.图像的 ...
-
蓝桥杯-李白打酒-java
/* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...
-
Android 开发笔记___SD卡基本操作
package com.example.alimjan.hello_world; /** * Created by alimjan on 7/5/2017. */ import android.ann ...