yii2框架-restful的请求参数token验证(二十三)

最近出行市场发生一件大事:滴滴收购uber中国,两者正式联姻。
对于这次的收购合并,其实对于滴滴和uber来说都是双赢的。两者在市场的竞争中已经烧了不少的钱,而且uber的股东也建议TK(uber创始人)和滴滴讲和,不能在这样子无尽烧钱,毕竟人家投资人是要挣钱的。滴滴目前可以说是已经是国内的出行市场的老大了,乐视投资的”易到“已经是基本边缘化的了。
滴滴和uber的联姻都可以说明到过去的创业独角兽以前都是在百团甚至千团大战中为占领市场份额不断地烧钱竞争,但是每一个垂直行业最后能够活下来的也就那么几家,过度长期的烧钱是一个无尽洞,投资人也不是傻子。这两三年收购或者合并的独角兽包括,滴滴和快的(也就是现在滴滴出行),美团和大众点评,58同城和赶集,美丽说和蘑菇街,世纪佳缘和百合网,携程和去哪儿,阿里收购优酷,腾讯投资京东等等,这其实是在资本寒冬下,创业企业的抱团取暖,最终对于企业来说都是在增大各自领域的市场份额,回归商业的本质,公司步入正常的发展和运营管理轨道,提升服务品质及升级行业。
最近也有人说,滴滴可能会垄断市场,违反垄断法,这个还真的说不好!

ok,一些移动互联网周边事说完了,下面聊聊我们的yii2的restful的验证授权吧!
上两节我们说了restful的一些基础人们和自定义api的接口,但是我们通过http协议访问都是无状态的,服务端并不知道每一个请求是来自于那个用户的,所以有时我们访问一个接口,我们需要对访问的请求进行认证,认证通过了,才能继续往下访问,简单地说,这个验证也就是这个接口的网关,要往下执行必须要通过这个关口先。

yii2有三种的认证方式:
1、请求参数方式: access token
也即是当作API URL请求参数发送,例如 https://example.com/users?access-token=xxxxxxxx
2、HTTP 基本认证: 发送用户名username和password, 应用在access token可安全存在API使用端的场景.例如,API使用端是运行在一台服务器上的程序
3、OAuth 2: 使用者从认证服务器上获取基于 OAuth2协议的access token,然后通过 HTTP Bearer Tokens 发送到API 服务器。

下面直说前面两种.
第一种方式:
(1)因为RESTful APIs应为无状态的, 当yii\web\User::enableSession为false, 请求中的用户认证状态就不能通过session来保持。

在控制器中重写初始化函数,设置enableSession = false

public function init()
{
    parent::init();
    Yii::$app->user->enableSession = false;

(2)重写behaviors函数,配置authenticator,引入使用的认证方式。

yii\filters\auth\QueryParamAuth;
public function behaviors()
{
    $behaviors = parent::behaviors();
    $behaviors['authenticator'] = [
        'class' => QueryParamAuth::className(),
    ];
    return $behaviors;
}
behaviors()这个函数的是定义这个控制器类的行为,也就是每一次访问这个控制器的方法,都会执行这个behaviors中定义的各种行为,认证也是这个流程,我们访问一个api接口时,就会执行yii\filters\auth\QueryParamAuth的这个文件的authenticate()这个方法



(3)我们需要在配置文件中
'user' => [
    'identityClass' => 'api\models\User',
],


指定认证的model类,现在是在api\models\User这个类中,那么我们需要在api\models\User这个类中实现yii\web\IdentityInterface这个类中的所有定义的接口方法

<?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 {
	 /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'users';
    }
    /**
     * @inheritdoc
     */
    public static function findIdentity($id)
    {
        return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
    }

    /**
     * @inheritdoc
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }

    //这个就是我们进行yii\filters\auth\QueryParamAuth调用认证的函数,下面会说到。
    public function loginByAccessToken($accessToken, $type) {
    	//查询数据库中有没有存在这个token
        return static::findIdentityByAccessToken($token, $type);
    }

    /**
     * Finds user by username
     *
     * @param string $username
     * @return static|null
     */
    public static function findByUsername($username)
    {
        return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]);
    }


    /**
     * @inheritdoc
     */
    public function getId()
    {
        return $this->getPrimaryKey();
    }

    /**
     * @inheritdoc
     */
    public function getAuthKey()
    {
        return $this->auth_key;
    }

    /**
     * @inheritdoc
     */
    public function validateAuthKey($authKey)
    {
        return $this->getAuthKey() === $authKey;
    }
}
loginByAccessToken()这个函数是我们需要自己定义的函数,因为这个函数在yii\filters\auth\QueryParamAuth的认证类中会调用。
而findIdentityByAccessToken($token, $type = null)这个是接口函数,我们需要实现的,所以就在loginByAccessToken()这个函数中调用他去查询数据表中有没有对应的token存在,这个就是认证过程。

这样子整个认证的过程就已经完成了。例如现在我们的数据表user有一个token=xxxxxxxx,那么我们的客户端取得这个token,
我们只需要在访问的api附带这个token
http://xxxxxxx/api/v1/users?access-token=xxxxxxxxx;
那么这个url访问就会认证通过,可以返回用户列表,如果没有附带token,那么就会返回401,认证失败,不能往下执行。

下面我们来看一下yii\filters\auth\QueryParamAuth这个认证类:

<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\filters\auth;

/**
 * QueryParamAuth is an action filter that supports the authentication based on the access token passed through a query parameter.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class QueryParamAuth extends AuthMethod
{
    /**
     * @var string the parameter name for passing the access token
     */
    public $tokenParam = 'access-token';


    /**
     * @inheritdoc
     */
    public function authenticate($user, $request, $response)
    {
        $accessToken = $request->get($this->tokenParam);
        if (is_string($accessToken)) {
            $identity = $user->loginByAccessToken($accessToken, get_class($this));
            if ($identity !== null) {
                return $identity;
            }
        }
        if ($accessToken !== null) {
            $this->handleFailure($response);
        }

        return null;
    }
}

$tokenParam这个属性是设置url附带的token的参数key,我们可以在behaviors()这个函数中配置修改:

public function behaviors() {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => QueryParamAuth::className(),
            'tokenParam' => 'token'  //例如改为‘token’
        ];
        return $behaviors;
}
看看认证函数:
public function authenticate($user, $request, $response) {

}
$user其实就是在配置中user组件对应的api\models\User的实例,$request, $response分别是请求组件和响应组件
再看看authenticate()函数里面的这个函数
$identity = $user->loginByAccessToken($accessToken, get_class($this));

loginByAccessToken()函数也就是在api\models\User类中定义的函数,进行token的认证的。

认证通过后就返回return $identity;

那么大家又觉得奇怪,$user, $request, $response这三个参数在这个类中并没有定义,那么他们是从哪里来的呢?
其实我们可以看到这个类是继承yii\filters\auth\AuthMethod这个类的,里面有一个beforeAction()函数

public function beforeAction($action)
    {
        $response = $this->response ? : Yii::$app->getResponse();

        try {
            $identity = $this->authenticate(
                $this->user ? : Yii::$app->getUser(),
                $this->request ? : Yii::$app->getRequest(),
                $response
            );
        } catch (UnauthorizedHttpException $e) {
            if ($this->isOptional($action)) {
                return true;
            }

            throw $e;
        }

        if ($identity !== null || $this->isOptional($action)) {
            return true;
        } else {
            $this->challenge($response);
            $this->handleFailure($response);
            return false;
        }
    }

所以我们执行yii\filters\auth\AuthMethod\QueryParamAuth时,也就是执行beforeAction($action)函数,这个函数调用$this->authenticate()这个函数执行,也就是进行操作前的认证。

友情推荐

本人已开源基于swoole扩展实现的轻量级框架

https://github.com/bingcool/swoolefy




评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值