lovelilili 发表于 2020-10-15 19:14

从零开始搭建完整的全栈系统(四)——restfulApi用户的认证授权及用户注册

接上三篇:
从零开始搭建完整的全栈系统(一)——数据库设计及爬虫编写

从零开始搭建完整的全栈系统(二)——简单的WEB展示网站的搭建

从零开始搭建完整的全栈系统(三)——restfulApi的编写

在配置文件main.php中设置用户认证类,并注释掉cookies和session配置,因为Api客户端和WEB网站不同,通常不能使用cookies和session维持登录状态。

```php
'user' => [
            'identityClass' => 'common\models\User',
            'enableAutoLogin' => true,
            'enableSession' => false,
            //'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],
      ],
      /*'session' => [
            // this is the name of the session cookie used for login on the backend
            'name' => 'advanced-backend',
      ],*/
```

## 实现Api用户认证(登录功能)

复制一份common/models/UserLoginForm到common/models中并改名为ApiLoginForm,同时将remember me 和vitifyCode等相关功能取消,并重写login方法。代码如下:

```php
<?php
namespace api\models;

use common\models\User;
use Yii;
use yii\base\Model;

/**
* Login form
*/
class ApiLoginForm extends Model
{
    public $username;
    public $password;
    //public $rememberMe = true;
    public $vitifyCode;

    private $_user;

    public function attributeLabels() //属性labels
    {
      return [
            'username' => '用户名',
            'password' => '密码',
            //'rememberMe' => '记住我',
            //'vitifyCode' => '验证码',
      ];
    }
    /**
   * {@inheritdoc}
   */
    public function rules()
    {
      return [
            // username and password are both required
            [['username', 'password'], 'required'],
            // rememberMe must be a boolean value
            //['rememberMe', 'boolean'],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
            //['vitifyCode', 'captcha'], //验证码验证
      ];
    }

    /**
   * Validates the password.
   * This method serves as the inline validation for password.
   *
   * @Param string $attribute the attribute currently being validated
   * @param array $params the additional name-value pairs given in the rule
   */
    public function validatePassword($attribute, $params)
    {
      if (!$this->hasErrors()) {
            $user = $this->getUser();
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
      }
    }

    /**
   * Logs in a user using the provided username and password.
   *
   * @Return bool whether the user is logged in successfully
   */
    public function login()
    {
      if ($this->validate()) {
//            return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
            $access_token = $this->_user->generateAccessToken();
            $this->_user->expire_at=time()+3600*7*24;//设定access_token过期时间
            $this->_user->save();
            Yii::$app->user->login($this->_user,3600*7*24);
            return $access_token;
      } else {
            return false;
      }
    }

    /**
   * Finds user by []
   *
   * @return User|null
   */
    protected function getUser()
    {
      if ($this->_user === null) {
            $this->_user = User::findByUsername($this->username);
      }

      return $this->_user;
    }
}

```
在common/models User中实现generateAccessToken,代码如下:

```php
/**
   * 生成access_token
   * @return string
   * @throws \yii\base\Exception
   */

    public function generateAccessToken()
    {
      $this->access_token = Yii::$app->security->generateRandomString();
      return $this->access_token;
    }
```

实现UserController控制器,实现login方法:

```php
<?php

namespace api\controllers;

use api\models\ApiLoginForm;
use common\models\Article;
use yii\rest\ActiveController;

/**
* VodDetailController implements the CRUD actions for VodDetail model.
*/
class UserController extends ActiveController
{

    public $modelClass = 'common\models\User';
    public function actionLogin()
    {
      $model = new ApiLoginForm();
      /* $model->username = $_POST['username'];
         $model->password = $_POST['password'];*/
      //使用getBodyParams处理POST请求
      $model->load(\Yii::$app->getRequest()->getBodyParams(),'');
      if ($model->login()) {
            return ['access_token' => $model->login()];
      } else {
            $model->validate();
            return $model;
      }
    }
}

```

配置好url美化:

```php
[
                  'class' => 'yii\rest\UrlRule',
                  'controller' => 'user',
                  'pluralize' => false,//访问资源不需要加s
                  'extraPatterns' => [
                        'POST login' => 'login',
                        'POST signup' => 'signup',
                  ],
                ]
```
测试登录api顺利获得accessToken:
![测试登录api顺利获得accessToken:](https://img-blog.csdnimg.cn/20200911151206668.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDA3NTk1Mg==,size_16,color_FFFFFF,t_70#pic_center)

## 认证状态的维持


维持认证状态,就是客户端如何⽤accesstoken这个令
牌,去访问服务端所提供的服务。
1,添加QueryParamAuth过滤器,在VideoDetailController:

```php
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'authenticatior' => [
'class' => QueryParamAuth::className()
]
]);
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200911155300795.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDA3NTk1Mg==,size_16,color_FFFFFF,t_70#pic_center)
可以看出认证已经生效。

我们带上有效的access-token去测试:
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020091115544735.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDA3NTk1Mg==,size_16,color_FFFFFF,t_70#pic_center)
提示我们,要想顺利通过验证必须在\common\\models\\User.php 实现个findIdentityByAccessToken方法。实现该方法:

```php
public static function findIdentityByAccessToken($token, $type = null)
    {
      return static::find()
            ->where(['access_token'=>$token,'status'=>self::STATUS_ACTIVE])
            ->andWhere(['>','expire_at',time()])
            ->one();
    }
```

再次带上access-token去测试,可以看出顺利得到想要的数据了:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200911160218704.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDA3NTk1Mg==,size_16,color_FFFFFF,t_70#pic_center)
我们还可以指定需要认证才能访问的动作。比如列表页不需要认证,详情需要:

```php
public function behaviors()
    {
      return ArrayHelper::merge(parent::behaviors(), [
            'authenticatior' => [
                'class' => QueryParamAuth::className(),
                'only' => ['view'],
            ],

      ]);
    }
```

Yii RestfulApi认证可以参考:(https://www.yiichina.com/doc/guide/2.0/rest-authentication)

过滤器可以参考:[过滤器](https://www.yiichina.com/doc/guide/2.0/structure-filters)

## 用户注册实现
复制一份frontend/models/SignupForm到common/models中并改名为ApiSignupForm,并修改 代码如下:

```php
<?php
namespace api\models;

use common\models\User;
use yii\base\Model;

/**
* Signup form
*/
class ApiSignupForm extends Model
{
    public $username;
    public $email;
    public $password;
    public $password_repeat;


    /**
   * @inheritdoc
   */
    public function rules()
    {
      return [
            ['username', 'trim'],
            ['username', 'required'],
            ['username', 'unique', 'targetClass' => '\common\models\User', 'message' => '用户名已被占用.'],
            ['username', 'string', 'min' => 2, 'max' => 255],

            ['email', 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            ['email', 'string', 'max' => 255],
            ['email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'Email已被占用.'],

            ['password', 'required'],
            ['password_repeat', 'required'],
            ['password', 'string', 'min' => 6],
            ['password_repeat','compare','compareAttribute'=>'password','message'=>'两次输入的密码不一致!'],
            /*['realname','required'],
            ['realname','string','max'=>128],*/
      ];
    }

    public function attributeLabels()
    {
      return [
            'username' => '用户名',
            //'realname' => '姓名',
            'password' => '密码',
            'password_repeat'=>'重复密码',
            'email' => '电子邮箱',
      ];
    }

    /**
   * Signs user up.
   *
   * @return User|null the saved model or null if saving fails
   * @throws \yii\base\Exception
   */
    public function signup()
    {
      if (!$this->validate()) {
            return null;
      }
      
      $user = new User();
      $user->username = $this->username;
      $user->email = $this->email;
      //$user->realname=$this->realname;
      $user->setPassword($this->password);
      $user->generateAuthKey();
      
      return $user->save() ? $user : null;
    }
}

```

在UserController中实现 signup方法:

```php
public function actionSignup()
    {
      $model = new ApiSignupForm();
      $model->load(\Yii::$app->getRequest()->getBodyParams(),'');
      if ($model->signup()) {
            return ['result' =>'注册成功'];
      } else {
            $model->validate();
            return $model;
      }
    }
```
配置Url美化:

```php
[
                  'class' => 'yii\rest\UrlRule',
                  'controller' => 'user',
                  'pluralize' => false,//访问资源不需要加s
                  'extraPatterns' => [
                        'POST login' => 'login',
                        'POST signup' => 'signup',
                  ],
                ]
```

测试:
!(https://img-blog.csdnimg.cn/20200911165907793.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDA3NTk1Mg==,size_16,color_FFFFFF,t_70#pic_center)

另外关于restful Api的限速问题可以自行查阅yii框架微服务知识。

ytw6176 发表于 2020-10-15 20:08

Yii框架没摸过,不过看着咋都差不多

gogogoat 发表于 2020-10-16 09:53

学习了,感谢分享
页: [1]
查看完整版本: 从零开始搭建完整的全栈系统(四)——restfulApi用户的认证授权及用户注册