用Yii2构建自己的API框架(五、4增加User账号登录接口)

215 阅读2分钟

提示

本教程相关代码托管在Gitee:gitee.com/toshcn/micr…

创建User账号登录表单,通过表单接收前端输入,验证输入的数据,返回用户接口令牌,后续使用用户令牌调用需要登录的其它接口。

修改User模型类,增加通过手机号码查找用户模型方法

打开common/models/User.php文件,修改内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/16 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace common\models;

use Yii;
use yii\db\ActiveRecord; // 活动记录模型
use yii\filters\RateLimitInterface; // 速率限制接口
use yii\web\IdentityInterface; // 用户身份接口
// ...
class User extends ActiveRecord implements IdentityInterface, RateLimitInterface
{
    // ...
    
    /**
     * 登录成功后事件处理器
     * @param object $event 事件对象
     * @return bool
     */
    public static function afterLoginHandler($event) // &$event 修改为$event
    {
        return true; // todo
    }
    
    // 增加如下方法
    
    /**
     * 通过手机号码查找用户
     * @param number $mobile 手机号码
     * @param int $countryCode 国家代码
     * @return User|null
     */
    public static function findByMobile($mobile, $countryCode = 86)
    {
        return static::findOne(['mobile' => $mobile, 'country_code' => (int) $countryCode]);
    }
    
    /**
     * 登录成功返回登录令牌等用户信息
     * @throws \yii\base\Exception
     */
    public function loginInfo()
    {
        // 更新令牌
        $this->generateAccessToken();
        return [
            'uid' => (int) $this->id,
            'nickname' => $this->nickname,
            'avatar' => $this->avatar,
            'token' => $this->access_token,
            'tokenExpire' => $this->access_token_expire
        ];
    }
}

创建User账号登录表单类

在项目api/modules/v1/forms目录下创建UserLoginForm.php文件,文件内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/29 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace api\modules\v1\forms;

use Yii;
use common\models\User;
use common\models\BaseModel;

/**
 * User模型登录表单
 * Class UserForm
 * @package api\modules\v1\forms
 */
class UserLoginForm extends BaseModel
{
    public $mobile;                 // 用户账号手机号
    public $countryCode = 86;       // 用户账号手机号国家代码: 86(中国)
    public $password;               // 用户账号密码

    /** @var null|User  */
    private $_user = null;          // 用户对象

    /**
     * 表单字段规则
     * @return array
     */
    public function rules()
    {
        return [
            [['mobile', 'password'], 'trim'],
            [['countryCode'], 'number', 'skipOnError' => true],
            [['mobile', 'countryCode', 'password'], 'required'],
            ['mobile', 'validateMobile', 'skipOnError' => true],     // 行内验证器 检测手机号码
            ['password', 'validatePassword', 'skipOnError' => true], // 行内验证器 检测密码强度
        ];
    }

    /**
     * 检测手机号
     * @param string $attribute 当前检测的字段名
     * @param array $params 以名-值对形式提供的额外参数
     * @param \yii\validators\InlineValidator $validator 当前行内验证器 InlineValidator实例。此参数自版本 Yii 2.0.11 起可用。
     */
    public function validateMobile($attribute, $params, $validator)
    {
        if (!$this->hasErrors()) {
            if (!Yii::$app->utils->regExp($this->$attribute, 'cn_mobile')) {
                // 检测手机号码格式
                $validator->addError($this, $attribute, Yii::t('common', 'The mobile "{value}" is invalid.'));
            } else {
                // 检测手机号是否已注册
                $this->_user = User::findByMobile($this->mobile, $this->countryCode);
                if (empty($this->_user)) {
                    $validator->addError($this, $attribute, Yii::t('common', 'The account is not registered.'));
                }
            }
        }
    }

    /**
     * 检测密码
     * @param string $attribute 当前检测的字段名
     * @param array $params 以名-值对形式提供的额外参数
     * @param \yii\validators\InlineValidator $validator 当前行内验证器 InlineValidator实例。此参数自版本 2.0.11 起可用。
     */
    public function validatePassword($attribute, $params, $validator)
    {
        if (!$this->hasErrors()) {
            if (empty($this->_user) || !$this->_user->validatePassword($this->$attribute)) {
                $this->addError($attribute, Yii::t('common', 'Password verification failed.'));
            }
        }
    }

    /**
     * 返回用户对象
     * @return null|@common\models\User
     */
    public function getUser()
    {
        return $this->_user;
    }

    /**
     * 登录账号
     * @return bool
     */
    public function login()
    {
        // rules()规则都验证通过
        if ($this->validate()) {
            if (Yii::$app->getUser()->login($this->_user)) {
                return true;
            }
        }
        return false;
    }
}

修改AccountControll账号控制器类,增加账号登录方法

打开api/modules/v1/controllers目录下的AccountControll.php文件,文件修改内容如下:

<?php /** @noinspection PhpUndefinedFieldInspection */

/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/22 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace api\modules\v1\controllers;

use Yii;
use api\controllers\ApiController;
use api\modules\v1\forms\UserRegisterForm;
use api\modules\v1\forms\UserLoginForm;

/**
 * 账号登录,注册相关控制器
 * Class AccountController
 * @package api\modules\v1\controllers
 */
class AccountController extends ApiController
{
    // ...
    // 增加的方法
    
    /**
     * @return mixed
     */
    public function actionLogin()
    {
        $this->validateIsPost();
        $form = new UserLoginForm();
        // 从post中取出数据,赋值给登录表单对应字段
        if ($form->load(Yii::$app->getRequest()->post(), '') && $form->login()) {
            return Yii::$app->api->success($form->getUser()->loginInfo());
        }
        if ($form->hasErrors()) {
            return Yii::$app->api->error(parent::ERROR_CODE_FORM_FIELD_ERROR, $form->getFirstOneError());
        }
        return Yii::$app->api->error(parent::ERROR_CODE_FORM_ERROR, Yii::t('common', 'Account login failed.'));
    }
}

修改api配置requry组件

关闭表单csrf检测

// ...
return [
    // ...
    'components' => [
        // ...
        
        // 配置api接收请求组件
        'request' => [
            'csrfParam' => '_csrf-api',
            // 关闭csrf检测 接口不需要 增加的配置
            'enableCsrfValidation' => false,
            'enableCsrfCookie' => false,
            // 配置api接收JSON数据
            'parsers' => [
                'application/json' => 'yii\web\JsonParser',
            ]
        ],
    ]   
]

测试接口

用Postman模拟请求www.mysite.local/v1/account/… mysite.local 替换为实际的主机名,设置请求标头content-typeapplication/json; charset=UTF-8, 提交表单如:

{
    "mobile": "13188888888",
    "password": "Test12345678"
}

将会看到如下内容:

{
    "status": 1,
    "code": 1,
    "message": "success",
    "data": {
        "uid": 1,
        "nickname": "test",
        "avatar": "",
        "token": "4383fb0cc3aa122d8aa3550e993d936c",
        "tokenExpire": 1651840095
    }
}