在完成了上一步的最基本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文档中关于安全的部分

发表评论

电子邮件地址不会被公开。 必填项已用*标注