LaravelCode 2018-06-27 17:12:00 10904次浏览 10条评论 12 8 0

Yii 2.0 RESTful API 认证教程

隔了怎么长时间,终于到了 Yii2.0 RESTful API 认证介绍了。
继上篇Yii2.0 RESTful API快速搭建教程

废话不多说,直接正文开始

认证介绍

和Web应用不同,RESTful APIs 通常是无状态的, 也就意味着不应使用 sessionscookies, 因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过 sessionscookies 维护, 常用的做法是每个请求都发送一个秘密的 access token 来认证用户, 由于 access token 可以唯一识别和认证用户,API 请求应通过 HTTPS 来防止man-in-the-middle (MitM) 中间人攻击.

认证方式

  • HTTP 基本认证 :access token 当作用户名发送,应用在access token可安全存在API使用端的场景, 例如,API使用端是运行在一台服务器上的程序。
  • 请求参数: access token 当作API URL请求参数发送,例如 https://example.com/users?access-token=xxxxxxxx, 由于大多数服务器都会保存请求参数到日志, 这种方式应主要用于JSONP 请求,因为它不能使用HTTP头来发送 access token
  • OAuth 2 : 使用者从认证服务器上获取基于 OAuth2 协议的 access token, 然后通过 HTTP Bearer Tokens 发送到 API 服务器。

上方进行简单介绍,内容来自 Yii Framework 2.0 权威指南

实现步骤

配置

我们都知道 Yii2.0 默认的认证类都是 User,前后台都是共用一个认证类,因此我们要把API 认证类 单独分离出来,达到前、后、API都分离,
继上一章:(这里暂时使用默认User数据表,正式环境请分离不同的数据表来进行认证)

打开 api\config\main.php

配置 user 应用组件:

  • 设置 identityClass 属性为哪个认证类
  • 设置 enableSession 属性为 false
  • 设置 enableAutoLogin 属性为 true

    session 组件注释掉,或删掉

    'user' => [
              'identityClass' => 'api\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\models\User.php 实现认证类,继承 IdentityInterface

common\models\User 类拷贝到 api\models\目录下,修改命名空间为api\models

<?php
namespace api\models;

use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
...
class User extends ActiveRecord implements IdentityInterface
{
    ...
    ...
}

common\models\LoginForm.php 类拷贝到api\models\目录下,修改命名空间,并重写login方法:

<?php
namespace api\models;

use Yii;
use yii\base\Model;
...
...

public function login()
{
    if ($this->validate()) {
        $access_token=$this->_user->generateAccessToken();
        $this->_user->save();
        return $access_token;
    } else {
        return false;
    }
}

上方代码给User模型添加了一个generateAccessToken()方法,因此我们到api\models\User.php中添加此方法

namespace api\models;

use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
...
...
class User extends ActiveRecord implements IdentityInterface
{
    ...
    ...
    
    /**
     * 生成accessToken字符串
     * @return string
     * @throws \yii\base\Exception
     */
    public function generateAccessToken()
    {
        $this->access_token=Yii::$app->security->generateRandomString();
        return $this->access_token;
    }
}

接下来打开 之前的User 控制器编写登录方法

use api\models\LoginForm;
...
... //省略一些代码


/**
 * 登陆
 * @return array
 * @throws \yii\base\Exception
 * @throws \yii\base\InvalidConfigException
 */
public function actionLogin()
{
    $model = new LoginForm();
    if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->login()) {
        return [
            'access_token' => $model->login(),
        ];
    } else {
        return $model->getFirstErrors();
    }
}
...

最后新增一条URL规则

打开 api\config\main.php 修改 components属性,添加下列代码:

'urlManager' => [
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [
        ['class' => 'yii\rest\UrlRule', 
            'controller' => 'user',
            'extraPatterns'=>[
                'POST login'=>'login',
            ],
        ],
    ],
]

使用一个调试工具来进行测试 http://youdomain/users/login 记住是POST 请求发送,假如用POSTMAN有问题的话指定一下 Content-Type:application/x-www-form-urlencoded

ok,不出意外的话,相信你已经可以收到一个access_token了,接下来就是如何使用这个token,如何维持认证状态,达到不携带这个token将无法访问,返回401

维持认证状态

实现认证只需两步:

  1. 在你的 REST 控制器类中配置 authenticator 行为来指定使用哪种认证方式
  2. 在你的 user identity class 类中实现 yii\web\IdentityInterface::findIdentityByAccessToken() 方法.

接下来我们围绕这两步来实现:

添加一个REST控制器

  • 因我这里暂未设计其他数据表 所以我们暂且还使用User 数据表吧
  1. api\controllers\新加一个控制器 命名为 ArticleController 并继承 yii\rest\ActiveController,配置认证方式代码:代码如下:
<?php
namespace api\controllers;

use yii\rest\ActiveController;
use Yii;
use yii\filters\auth\CompositeAuth;
use yii\filters\auth\HttpBasicAuth;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;

class ArticleController extends ActiveController
{
    public $modelClass = 'api\models\User';
    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => CompositeAuth::className(),
            'authMethods' => [
                HttpBasicAuth::className(),
                HttpBearerAuth::className(),
                QueryParamAuth::className(),
            ],
        ];
        return $behaviors;
    }
}

注意:这个控制器并非真正的Article,实则还是User

  1. 实现 findIdentityByAccessToken() 方法:

打开 api\models\User.php 重写 findIdentityByAccessToken() 方法

...
...
class User extends ActiveRecord implements IdentityInterface
{
    ...
    ...
    
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }
    ...
}
  1. 为刚才新加的控制器添加路由规则(ps:好像多了一步......)

修改 api\config\main.php

'urlManager' => [
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [
        ['class' => 'yii\rest\UrlRule',
            'controller' => 'user',
            'extraPatterns'=>[
                'GET send-email'=>'send-email'
                'POST login'=>'login',
            ],
        ],
        ['class' => 'yii\rest\UrlRule',
            'controller' => 'article',
            'extraPatterns'=>[

            ],
        ],
    ],
]

接下来访问一下你的域名 http://youdomain/articles,不携带任何参数是不是返回 401了?

ok,这里介绍两种访问方式,一种是URL访问,另一种是通过header 来进行携带

  1. http://youdomain/articles?access-token=y3XWtwWaxqCEBDoE-qzZk0bCp3UKO920
  2. 传递 header头信息
    Authorization:Bearer y3XWtwWaxqCEBDoE-qzZk0bCp3UKO920
    

    注意 Bearer 和你的token中间是有 一个空格的,很多同学在这个上面碰了很多次

好啦,基于YII2.0 RESTful 认证就此结束了,

更过完整的功能 请移步官方文档
授权验证
另外还有速率验证,就自行发觉吧
另外,如果看不懂,或者写的不好,请移步 魏曦 老师的视频教程,本人所有内容都是跟随 魏曦老师 学的
魏曦教你学

写完认证发现我们的接口返回的数据不是很直观,现实生活中通常也不是这样子的,我们可能会返回一些特定的格式

自定义响应内容

打开 api\config\main.phpcomponents数组里面添加如下内容分


'response' => [
    'class' => 'yii\web\Response',
    'on beforeSend' => function ($event) {
        $response = $event->sender;
        $response->data = [
            'success' => $response->isSuccessful,
            'code' => $response->getStatusCode(),
            'message' => $response->statusText,
            'data' => $response->data,
        ];
        $response->statusCode = 200;
    },
],

这里的状态码统一设为 200 ,具体的可另行配置,假如登陆操作 密码错误或者其他,我们可以在控制器中这样使用:

    $response = Yii::$app->response;
    $response->setStatusCode(422);
    return [
        'errmsg' => '用户名或密码错误!'
    ];

水平有限,难免有纰漏,请不吝赐教,在下会感激不尽

觉得很赞
  • 评论于 2018-07-04 14:30 举报

    准备条件

    继上篇的 User 数据表,我们还需要增加一 个access_token 的字段,

    1. 直接在你的数据库中新增 access_token 字段。
    2. 使用数据迁移的方式

      • 进入项目根目录打开控制台输入以下命令:
         php yii migrate/create add_access_token_to_user
        
      • 打开 你的项目目录/console/migrations/m180704_054630_add_access_token_to_user.php 修改如下内容:
       public function safeUp()
       {
           $this->addColumn('user', 'access_token', $this->string());
       }
      
       public function safeDown()
       {
           $this->dropColumn('user', 'access_token');
       }
      
      • 执行迁移命令
         php yii migrate
        
  • 评论于 2018-08-13 14:06 举报

    写得不错,挺好的

    3 条回复
    评论于 2018-08-13 16:10 回复

    感谢认同,

    评论于 2018-08-13 22:24 回复

    主要是我也在看他的视频,哈哈

    评论于 2018-08-14 08:51 回复

    魏曦老师很厉害的,通俗易懂

  • 评论于 2018-10-18 15:49 举报

    知道loginFrom 里login方法的$this->validate()一直返回false是什么原因吗

    1 条回复
    评论于 2018-10-18 21:50 回复

    你打印下model得错误消息呗

  • 评论于 2018-11-18 18:34 举报

    为什么 我返回的token为空

  • 评论于 2019-02-15 17:03 举报

    认证只能认证user表吗?其他的怎么加认证?

    1 条回复
    评论于 2019-02-15 18:20 回复

    实现 identityClass 认证类就好,可以自定义用户表

  • 评论于 2019-02-27 13:41 举报

    你好,我看了一下,还没有去尝试。
    根据你的说明,是user表新增一个access_token字段,用户登录时生成一个access_token值,保存入库并返回给用户。
    用户再次访问时,必须发送access_token值,来验证登录。没有或者错误,就需要重新登录。

    那如果允许同一个账号在两台设备上同时登录呢???毕竟access_token只能存一个值

    1 条回复
    评论于 2019-02-27 20:30 回复

    不允许多设备登陆,一端登陆,另一端就得下线

  • 评论于 2019-03-04 15:10 举报

    我已经配置好了。

    1 条回复
    评论于 2019-03-04 16:15 回复

    可以,可以

  • 评论于 2019-09-04 10:41 举报

    能否简单讲下怎么登陆获取token过程么?

    1 条回复
    评论于 2019-09-04 18:26 回复

    其实就是 User 类里面的 findIdentityByAccessToken 方法,看下这个方法你就知道了

  • 评论于 2020-05-09 15:27 举报

    使用调试工具测试,一直提示用户名或密码错误;该账号密码在前台可以登录的,请问这是什么原因呢

    2 条回复
    评论于 2020-05-10 22:11 回复

    @let it be 我的天,自己回复这么多嘛

    评论于 2020-05-19 15:24 回复

    哈哈,不好意思点错了

  • 评论于 2021-10-13 10:10 举报

    学习了 哈哈哈 高手

您需要登录后才可以评论。登录 | 立即注册