为 Yii2 RESTful API 添加用户登录(二)
在完成了上一步的最基本RESTfulAPI之后,我们需要给后面的api加一个登陆验证功能。
大体分为两步
– 实现token验证
– 给用户登录设置一个token获取接口。
实现用户鉴权的步骤
在Yii官方文档中已经有关于鉴权的部分。
大体分为以下2个部分。
– 在app/model/User中实现findIdentityByAccessToken() 方法,详见此
– 在需要rest的controller中指定authenticator
认证方式
如果你想禁用session那么就要在对应接口的controller中指定
\Yii::$app->user->enableSession = false;
实现一个BaseActivController方便调用
依据官方文档的方法,需要在每个需要验证的controller中指定如上的authenticator
属性,为了方便起见我们实现一个基类,然后在实现对应接口controller
时,继承基类BaseActivController
即可。
步骤如下:
- 使用gii工具或者手动,在
app/controller/
目录下新建BaseActiveController.php
文件 - 指定命名空间
namespace app\controllers
,根据官方稳定引用所需的类。 - 在
behavior()
中指定authenticator
属性,返回的数组就是指定的登录鉴权方式
<?php
namespace app\controllers;
use yii\filters\auth\CompositeAuth;
use yii\filters\auth\HttpBasicAuth;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;
use yii\rest\ActiveController;
use Yii;
//需要继承rest格式的ActiveController才能使用请求头来操作数据库
class BaseActiveController extends ActiveController
{
public $post = null;
public $get = null;
public $user = null;
public $userId = null;
public function init()
{
parent::init();
Yii::$app->user->enableSession = false;
}
public function behaviors()
{
$behaviors = parent::behaviors();
//指定用户鉴权方式
$behaviors['authenticator'] = [
'class' => CompositeAuth::className(),
'authMethods' => [
HttpBasicAuth::className(),
HttpBearerAuth::className(),
QueryParamAuth::className(),
]
];
// 数据返回类型设置
$behaviors['contentNegotiator']['formats']['application/json'] = 'json';
$behaviors['contentNegotiator']['formats']['application/xml'] = 'json';
return $behaviors;
}
// 可以返回psot/get、用户identity等数据,方便使用Yii::$app->post->字段名取用
// public function beforeAction($action)
// {
// parent::beforeAction($action);
//
// $this->post = yii::$app->request->post();
// $this->get = yii::$app->request->get();
// $this->user = yii::$app->user->identity;
// $this->userId = Yii::$app->user->id;
//
// return $action;
// }
// 配置 数据序列化 --响应主体内包含分页信息 可以选择不使用
+ public $serializer = [
+ 'class' => 'yii\rest\Serializer',
+ 'collectionEnvelope' => 'items',
+ ];
}
实现app/models/User.php
中的findIdentityByAccessToken()
在上一篇中我们已经实现了使用数据库保存用户数据并实现了findIdentityByAccessToken()
方法,此方法会用户LoginForm.php中的用户登录鉴权。
// app/models/User.php
public static function findIdentityByAccessToken($token, $type = null)
{
return static::findOne(['accessToken' => $token]);
}
对已有接口进行改造
只需要在需要鉴权的接口中将XXXController
继承BaseActiveController
即可。
namespace app/controller;
use Yii;
use app\models\YourModel;
use BaseActiveController;
class YourContrller extends BaseActiveController {
//指定你需要rest的引用的表
public $modelClass = 'app\models\YourModel';
//
....你需要实现或者重写的配置
}
关于分页信息的显示
我们有时有希望返回信息进行显示,官方对rest提供了见到的配置方式,只需要指定serializer
属性即可。如果你想自定义分页等信息,详见Pagination
部分,顺便提一句我们甚至可以在model中实现分页,对于活动记录而言,model层返回的均为一个数组(切记返回的是个数组,可以使用ArrayHelper助手类格式化成数组)
我们选择,官方文档中建议的rest响应格式化方法,只需要在对应controller中配置serializer
属性
// path: app/controller/YourController
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
我们不需要实现Veiw层部分就可以使返回的json对象拥有如下格式。
{
"items": [
+
+
+
],
"_links": {
"self": {
"href": "http://www.xxx.com/index.php/users?page=1"
}
},
"_meta": {
"totalCount": 9,
"pageCount": 1,
"currentPage": 1,
"perPage": 20
}
}
使用postman在Aute下配置鉴权方式为oauth2.0、
access-token的获取
以上完成了api的鉴权,所以需要一个获取token的接口,接收post的数据。
很简单,只需要接收用户的post请求,然后去查表并返回结果就可以
随便实现一个,如果有需要(比如速率限制等),可以根据自己需求写。
贴一个我随便写的
<?php
namespace app\controllers;
use app\models\User;
use yii\helpers\ArrayHelper;
use yii\filters\auth\QueryParamAuth;
use app\models\LoginForm;
use yii\web\IdentityInterface;
use Yii;
use yii\web\Response;
class ProfileController extends \yii\web\Controller
{
public function init()
{
$this->enableCsrfValidation = false;
}
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'authenticator' => [
'class' => QueryParamAuth::className(),
// 配置接口在login和sigup-test中不验证
'optional' => [
'login',
],
]
]);
}
public function actionLogin()
{
//格式化响应,否则会出现400错误
Yii::$app->response->format = Response::FORMAT_JSON;
$username = Yii::$app->request->post('username');
$password = Yii::$app->request->post('password');
$psdHandler = User::find()->where(['username' => $username])->asArray()->all();
if (!empty($psdHandler) && $psdHandler[0]['password'] == $password) {
return [
"username" => $username,
"token" => $psdHandler[0]['accessToken'],
"data" => $psdHandler[0],
];
} else {
return ["msg" => " username or password false "];
}
}
}
使用
curl -H "Content-Type:application/json" -X POST -d '{"user": "admin", "passwd":"password"}' http://127.0.0.1:8000/profile/login
=>
{
"username":"admin",
"password":"password",
"token":"xxx32位hash"
}
到此为止就实现了个简单的token获取接口,但是没有速率验证,也没有数据响应验证,是个只能在本地使用的demo,万幸的事Yii框架对于数据提交和返回提供了一套完整的controller中的防范方式,详见Yii文档中关于安全的部分