CakePHP 3 REST API身份验证,同时仍使用现有控制器

时间:2016-03-15 09:26:26

标签: api rest authentication cakephp

我在CakePHP 3中为移动设备实现REST API身份验证时遇到了问题。

阅读认证官方CakePHP 3文档:

http://book.cakephp.org/3.0/en/controllers/components/authentication.html#id1

它说我可以使用无状态基本身份验证而不是表单身份验证。但是,我也在其中一个论坛中读到你不能同时使用基本身份验证和表单身份验证。

我还研究过使用REST API的前缀路由。

http://www.bravo-kernel.com/2015/04/how-to-prefix-route-a-cakephp-3-rest-api/

通过这样做,我现在可以对前缀路由执行基本身份验证,对非前缀路由执行表单身份验证。但是,这需要在其前缀命名空间中定义控制器,这违反了CakePHP保持代码干燥的概念;因为我的控制器已经包含了适用于网络和移动设备的逻辑。

如果我只是使用表单身份验证,我可以从我的手机创建一个POST请求到Web系统,但响应将是HTML格式。解析整个HTML格式不是一个好方法。

我被困住了,不知道该怎么办。我整个下午一直在研究,但仍未能找到解决方案。

我想要的是能够通过我的移动应用程序进行身份验证,同时仍然使用相同的控制器。

编辑:解决方法

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Auth', [
            'authorize' => 'Controller',
            'authenticate' => [
                'Form' => [
                    'fields' => [
                        'username' => 'username',
                        'password' => 'password'
                    ]
                ]
            ],
            'loginAction' => ['controller' => 'Users', 'action' => 'login'],
            'unauthorizedRedirect' => $this->referer()
        ]);
    }

    public function beforeFilter(Event $event)
    {
        if($this->request->params['_ext'] === 'json')
        {
            $this->Auth->config('authenticate', ['Basic' => ['userModel' => 'Users']]);
            $this->Auth->config('storage', 'Memory');
            $this->Auth->config('unauthorizedRedirect', false);
        }
        return parent::beforeFilter($event);
    }
}

通过有条件地将基本身份验证添加到身份验证配置来管理以使其工作。

然而,这不是一个完美的解决方案,因为它肯定有陷阱。

这只能在以下假设下运作:

  1. 参考这篇文章:CakePHP将根据身份验证方案的顺序执行优先级。由于Form Auth在Basic Auth之前出现,如果Form Auth成功,Basic Auth将不会执行。

    CakePHP 2.1 - As a web application and REST service with Authentication

  2. 通常,网络用户不会访问json文件扩展名的网址,因此用户将使用表单身份验证重定向到用户登录页面。

  3. 通过表单身份验证对用户进行身份验证后,即使用户使用AJAX间接访问以.json结尾的URL,基本身份验证也不会弹出。

  4. 还有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

您可以使用basic authentication来做到这一点。对于网络,您必须使用form authentication;对于REST API,您必须使用basic authentication。 time.i已经实现了,下面是工作示例。

   public function initialize() {
        parent::initialize();
        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');
        $url = $this->request->url;
        $ext = $this->request->params['_ext'];
        if ((isset($ext) && $ext == 'json') && (!($this->request->is('ajax')) && ($url != 'users/loginapi.json')) ) {    
            $this->loadComponent('Auth', [
                'authenticate' => [
                    'Basic' => [
                        'fields' => ['username' => 'email', 'password' => 'api_key'],
                        'userModel' => 'Users',
                        'scope' => ['status' => 'A']
                    ],
                ],
                'storage' => 'Memory',
                'unauthorizedRedirect' => false
            ]);
        } else {
            $this->loadComponent('Auth', [
                'authenticate' => [
                    'Form' => [
                        'fields' => ['username' => 'email_or_mobile', 'password' => 'password'],
                        'userModel' => 'Users',
                        'scope' => ['status' => 'A'],
                        'finder' => 'auth'
                    ]
                ],
                'storage' => 'Session',
                //'unauthorizedRedirect' => false,
                'loginRedirect' => [
                    'controller' => 'Users',
                    'action' => 'index'
                ],
                'logoutRedirect' => [
                    'controller' => 'Users',
                    'action' => 'login'
                ],
                'authError' => false
            ]);
        }
    }