Yii2 RESTful API 学习笔记第三节:登录认证与状态维持

第一步:配置文件main.php

'components' => [
    'user' => [
        'identityClass' => 'api\models\User',  // 用户认证类
        'enableAutoLogin' => true,
        'enableSession' => false,
    ],
    'urlManager' => [
        'rules' => [
            [
                'class' => 'yii\rest\UrlRule',
                'controller' => 'site',
                'pluralize' => false,
                'extraPatterns' => [  // 新增其它操作列表
                    'POST login' => 'login',
                ],
            ],
        ],
    ],
],

第二步:修改表结构,添加字段access_tokenaccess_token

`access_token` varchar(32) DEFAULT NULL COMMENT '认证令牌',
`expire_at` int(11) DEFAULT NULL COMMENT '认证过期时间戳',

第三步:用户认证类api\models\User.php

namespace api\models;

use Yii;

class User extends \common\models\User
{
    /**
     * 生成访问令牌
     * @return string
     * @throws \yii\base\Exception
     */
    public function generateAccessToken()
    {
        $this->access_token = Yii::$app->security->generateRandomString();
        return $this->access_token;
    }

    /**
     * 判断访问令牌是否有效
     * @param string $token
     * @param null $type
     * @return User|null
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::find()
            ->where(['status' => self::STATUS_ACTIVE])
            ->andWhere(['access_token' => $token])
            ->andWhere(['>', 'expire_at', time()])
            ->limit(1)->one();
    }
}

第四步:登录验证类api\models\LoginForm.php

namespace api\models;

use yii\base\Model;

class LoginForm extends Model
{
    public $username;
    public $password;

    private $_user;


    public function rules()
    {
        return [
            [['username', 'password'], 'trim'],
            [['username', 'password'], 'required'],
            [['password'], 'validatePassword'],
        ];
    }

    public function attributeLabels()
    {
        return [
            'username' => '用户名',
            'password' => '密码',
        ];
    }

    public function validatePassword($attribute)
    {
        if(!$this->hasErrors()){
            $user = $this->getUser();
            if(!$user || !$user->validatePassword($this->password)){
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }

    public function login()
    {
        if($this->validate()){
            $accessToken = $this->_user->generateAccessToken();
            $this->_user->expire_at = time() + 3600;
            $this->_user->save(true, ['access_token', 'expire_at']);
            return $accessToken;
        }
        return false;
    }

    protected function getUser()
    {
        if($this->_user === null){
            $this->_user = User::findByUsername($this->username);
        }
        return $this->_user;
    }
}

第五步:控制器登录方法api\controllers\SiteController.php

namespace api\controllers;

use Yii;
use api\models\LoginForm;

class SiteController extends \yii\rest\Controller
{
    /**
     * @return LoginForm|array
     * @throws \yii\base\Exception
     */
    public function actionLogin()
    {
        $model = new LoginForm();
        $model->load(Yii::$app->request->post(), '');
        if($model->login()){
            return ['access_token' => $model->login()];
        }
        return $model;
    }
}

第六步、控制器过滤认证:

namespace api\controllers;

use Yii;
use yii\filters\auth\HttpBasicAuth;
use yii\filters\auth\QueryParamAuth;
use yii\rest\ActiveController;
use api\models\User;
use common\helpers\ArrayHelper;

class ArticleController extends ActiveController
{
    public function behaviors()
    {
        return ArrayHelper::merge(parent::behaviors(), [
            'authenticatior' => [
                // 请求参数认证方式, 参数名为`access-token`
                'class' => QueryParamAuth::className(),

                // HTTP 基本认证,
                /*'class' => HttpBasicAuth::className(),
                'auth' => function($username, $password){
                    $user = User::find()->where(['username' => $username])->limit(1)->one();
                    if($user->validatePassword($password)){
                        return $user;
                    }
                    return null;
                },*/
            ],
        ]);
    }
}

第七步、小程序访问受限:

//wxml:
<view bindtap='bindLoginTap'>登录</view>
<view bindtap='bindReadTap'>受限访问文章列表</view>

//js:
bindReadTap: function () {
  wx.request({
    url: 'http://tongmengcms2.ccc/api/articles?access-token=nuIoikmH-uow2iw3VL_R0sCg5UzUjQYbn',
    header: {
      'Content-Type': 'application/json'
    },
    method: 'GET',
    success: function (res) {
      console.log(res.data);
    }
  })
},