Yii2.0 RESTful API 认证教程

极客编码
• 阅读 5177

认证介绍

和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数据表,正式环境请分离不同的数据表来进行认证)

准备条件

继上篇的 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
浏览器打开前台目录 frontend 页面,点击注册账号,先注册一个账号

配置

打开 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 类中实现 yiiwebIdentityInterface::findIdentityByAccessToken()-detail) 方法.

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

添加一个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?acc...
  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' => '用户名或密码错误!'
    ];

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

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
4年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
4年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
极客编码
极客编码
Lv1
花开堪折直须折,莫待无花空折枝。
文章
3
粉丝
0
获赞
0