zoukankan      html  css  js  c++  java
  • 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数据表,正式环境请分离不同的数据表来进行认证)

    准备条件

    继上篇的 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
    

    配置

    打开 apiconfigmain.php

    配置 user 应用组件:

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

    session 组件注释掉,或删掉

    
    'user' => [
                'identityClass' => 'apimodelsUser',
                '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',
    //        ],
    

    编写 apimodelsUser.php 实现认证类,继承 IdentityInterface

    commonmodelsUser 类拷贝到 apimodels目录下,修改命名空间为apimodels

    
    <?php
    namespace apimodels;
    
    use Yii;
    use yiiaseNotSupportedException;
    use yiiehaviorsTimestampBehavior;
    use yiidbActiveRecord;
    use yiiwebIdentityInterface;
    ...
    class User extends ActiveRecord implements IdentityInterface
    {
        ...
        ...
    }
    

    commonmodelsLoginForm.php 类拷贝到apimodels目录下,修改命名空间,并重写login方法:

    
    <?php
    namespace apimodels;
    
    use Yii;
    use yiiaseModel;
    ...
    ...
    
    public function login()
    {
        if ($this->validate()) {
            $access_token=$this->_user->generateAccessToken();
            $this->_user->save();
            return $access_token;
        } else {
            return false;
        }
    }
    
    

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

    
    namespace apimodels;
    
    use Yii;
    use yiiaseNotSupportedException;
    use yiiehaviorsTimestampBehavior;
    use yiidbActiveRecord;
    use yiiwebIdentityInterface;
    ...
    ...
    class User extends ActiveRecord implements IdentityInterface
    {
        ...
        ...
        
        /**
         * 生成accessToken字符串
         * @return string
         * @throws yiiaseException
         */
        public function generateAccessToken()
        {
            $this->access_token=Yii::$app->security->generateRandomString();
            return $this->access_token;
        }
    }
    

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

    
    use apimodelsLoginForm;
    ...
    ... //省略一些代码
    
    
    /**
     * 登陆
     * @return array
     * @throws yiiaseException
     * @throws yiiaseInvalidConfigException
     */
    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规则

    打开 apiconfigmain.php 修改 components属性,添加下列代码:

    
    'urlManager' => [
        'enablePrettyUrl' => true,
        'enableStrictParsing' => true,
        'showScriptName' => false,
        'rules' => [
            ['class' => 'yii
    estUrlRule', 
                '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. apicontrollers新加一个控制器 命名为 ArticleController 并继承 yii estActiveController,配置认证方式代码:代码如下:
    
    <?php
    namespace apicontrollers;
    
    use yii
    estActiveController;
    use Yii;
    use yiifiltersauthCompositeAuth;
    use yiifiltersauthHttpBasicAuth;
    use yiifiltersauthHttpBearerAuth;
    use yiifiltersauthQueryParamAuth;
    
    class ArticleController extends ActiveController
    {
        public $modelClass = 'apimodelsUser';
        public function behaviors()
        {
            $behaviors = parent::behaviors();
            $behaviors['authenticator'] = [
                'class' => CompositeAuth::className(),
                'authMethods' => [
                    HttpBasicAuth::className(),
                    HttpBearerAuth::className(),
                    QueryParamAuth::className(),
                ],
            ];
            return $behaviors;
        }
    }
    

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

    1. 实现 findIdentityByAccessToken() 方法:

    打开 apimodelsUser.php 重写 findIdentityByAccessToken() 方法

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

    修改 apiconfigmain.php

    
    'urlManager' => [
        'enablePrettyUrl' => true,
        'enableStrictParsing' => true,
        'showScriptName' => false,
        'rules' => [
            ['class' => 'yii
    estUrlRule',
                'controller' => 'user',
                'extraPatterns'=>[
                    'GET send-email'=>'send-email'
                    'POST login'=>'login',
                ],
            ],
            ['class' => 'yii
    estUrlRule',
                '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 认证就此结束了,

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

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

    自定义响应内容

    打开 apiconfigmain.phpcomponents数组里面添加如下内容分

    
    
    'response' => [
        'class' => 'yiiwebResponse',
        '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' => '用户名或密码错误!'
        ];
    

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

    原文地址:https://segmentfault.com/a/1190000016368603

  • 相关阅读:
    使 Asp.net Core Hosting Bundle 立即生效
    Hosted Services require keep alive
    VS 高级保存选项,解决文件内容编码问题
    asp.net core localhost https 证书
    阿里云K8S下玩.NET CORE 3.1
    cmd 域名生效检测
    c# 通过win32 api 得到指定Console application Content
    .net framework msbuild环境搭建 (不装vs)
    Python常用模块——目录
    Python——爬虫进阶
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9971422.html
Copyright © 2011-2022 走看看