Базовая аутентификация в CakePHP

#cakephp #basic-authentication

#cakephp #базовая аутентификация

Вопрос:

Я пытаюсь настроить базовую аутентификацию для своего приложения CakePHP, чтобы я мог использовать его в качестве API для будущего мобильного приложения. Однако, если я передам следующее:

cameron:password@dev.driz.co.uk/basic/locked/

Где cameron — это имя пользователя, password — это пароль, а остальное — домен и приложение. locked это метод, который требует аутентификации. (очевидно, что в этом примере пароль неверен)

(Q1) В приглашении мне будет предложено ввести имя пользователя и пароль… но имя пользователя и пароль на самом деле правильные, как если бы я затем ввел их в приглашение, они работают… Почему это должно произойти?Разве я только что не передал имя пользователя и пароль?

Я не вижу ничего плохого в том, как я настроил это в CakePHP.

Я установил базовую аутентификацию в AppController как:

 public $components = array('Auth');

function beforeFilter()
{
    parent::beforeFilter();

    $this->Auth->authorize = array('Controller');
    $this->Auth->authenticate = array('Basic');
    $this->Auth->sessionKey = false;
    $this->Auth->unauthorizedRedirect = false;

}
  

(Q2) Несмотря на это, я установил для обоих сеансов значение false, а для перенаправления значение false, если пользователь отменяет запрос, они перенаправляются на страницу входа в систему? Есть идеи о том, как остановить это? В идеале я хочу отправить ответ JSON или код состояния 401 (в зависимости от того, является ли это AJAX-запросом или нет).

Итак, что-то вроде:

 if ($this->request->is('ajax')) {

    $response = json_encode(
            array(
                'meta'=>array(
                    'code'=>$this->response->statusCode(401),
                    'in'=>round(microtime(true) - TIME_START, 4)
                ),
                'response'=>array(
                    'status'=>'error',
                    'message'=>'401 Not Authorized'
                )
            )
        );

    // Handle JSONP
    if(isset($_GET['callback'])) {
        $response = $_GET['callback'] . '(' . $response . ')';
    }

    // Return JSON
    $this->autoRender = false;
    $this->response->type('json');
    $this->response->body($response);   

} else {

    header('HTTP/1.0 401 Unauthorized');

}
  

Но куда это приведет в логике приложения, чтобы показать это? Это должно произойти для ВСЕХ запрошенных методов, требующих аутентификации, и пользователь завершает или отменяет аутентификацию.

(Q3) Если вы вводите неправильные данные, вам просто снова отображается приглашение, пока вы не получите правильное имя пользователя / пароль или не нажмете «Отмена». Как я могу заставить его показывать ошибку?

Любые идеи по этим трем вопросам (помечены как номера дополнительных вопросов).

Обновление: вот как я отправляю заголовки в API:

 "use strict";jQuery.base64=(function($){var _PADCHAR="=",_ALPHA="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /",_VERSION="1.0";function _getbyte64(s,i){var idx=_ALPHA.indexOf(s.charAt(i));if(idx===-1){throw"Cannot decode base64"}return idx}function _decode(s){var pads=0,i,b10,imax=s.length,x=[];s=String(s);if(imax===0){return s}if(imax%4!==0){throw"Cannot decode base64"}if(s.charAt(imax-1)===_PADCHAR){pads=1;if(s.charAt(imax-2)===_PADCHAR){pads=2}imax-=4}for(i=0;i<imax;i =4){b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i 1)<<12)|(_getbyte64(s,i 2)<<6)|_getbyte64(s,i 3);x.push(String.fromCharCode(b10>>16,(b10>>8)amp;255,b10amp;255))}switch(pads){case 1:b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i 1)<<12)|(_getbyte64(s,i 2)<<6);x.push(String.fromCharCode(b10>>16,(b10>>8)amp;255));break;case 2:b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i 1)<<12);x.push(String.fromCharCode(b10>>16));break}return x.join("")}function _getbyte(s,i){var x=s.charCodeAt(i);if(x>255){throw"INVALID_CHARACTER_ERR: DOM Exception 5"}return x}function _encode(s){if(arguments.length!==1){throw"SyntaxError: exactly one argument required"}s=String(s);var i,b10,x=[],imax=s.length-s.length%3;if(s.length===0){return s}for(i=0;i<imax;i =3){b10=(_getbyte(s,i)<<16)|(_getbyte(s,i 1)<<8)|_getbyte(s,i 2);x.push(_ALPHA.charAt(b10>>18));x.push(_ALPHA.charAt((b10>>12)amp;63));x.push(_ALPHA.charAt((b10>>6)amp;63));x.push(_ALPHA.charAt(b10amp;63))}switch(s.length-imax){case 1:b10=_getbyte(s,i)<<16;x.push(_ALPHA.charAt(b10>>18) _ALPHA.charAt((b10>>12)amp;63) _PADCHAR _PADCHAR);break;case 2:b10=(_getbyte(s,i)<<16)|(_getbyte(s,i 1)<<8);x.push(_ALPHA.charAt(b10>>18) _ALPHA.charAt((b10>>12)amp;63) _ALPHA.charAt((b10>>6)amp;63) _PADCHAR);break}return x.join("")}return{decode:_decode,encode:_encode,VERSION:_VERSION}}(jQuery));

            $(document).ready(function(){

                var username = 'cameron';

                var password = 'password';

                $.ajax({
                    type: 'GET',
                    url: 'http://dev.driz.co.uk/basic/locked',
                    beforeSend : function(xhr) {
                        var base64 = $.base64.encode(username   ':'   password);
                        xhr.setRequestHeader("Authorization", "Basic "   base64);
                    },
                    dataType: 'jsonp',
                    success: function(data) {

                        console.log(data);

                    },
                    error: function(a,b,c) {
                        //console.log(a,b,c);
                    }
                });

            });
  

Ответ №1:

Вопрос 1

Вы не указываете, как вы посещаете защищенный URL-адрес (dev.driz.co.uk/basic/locked ). Вы уверены, что то, как вы это делаете, правильно настраивает заголовки запросов? Вам нужно кодировать имя пользователя / пароль в Base64.

Когда ваш первый запрос завершается неудачно, браузер выдает запрос, и успех означает, что браузер выполняет его правильно для вас во второй раз.

Посмотрите на заголовки ваших запросов, чтобы увидеть, что вы отправляете в первый раз, а что браузер отправляет во второй.

Вопрос 2

При сбое базовой аутентификации ваш сервер отправляет a 401 с заголовком WWW-Authenticate:Basic , который извлекается из браузера, и вам выдается приглашение. Это обычное поведение для всех браузеров с незапамятных времен, вы не можете это изменить.

Что касается вашей проблемы с отменой и перенаправлением на вход в систему, у Auth были некоторые изменения API после 2.4, которые выделены в книге. До версии 2.4 вы всегда перенаправлялись на loginAction.

Наконец, позвольте Auth выполнить всю работу за вас, правильно настроив ее, и не пытайтесь самостоятельно настраивать ответы, как в предлагаемом вами коде. Вы также никогда не должны использовать php header() в cakephp, используйте CakeRequest::header() вместо этого.

Вопрос 3

Отвечая на вопрос Q2, вы не можете заставить Basic и 401 не запускать запрос. Либо измените требуемый заголовок аутентификации (возможно, установив имя типа Basic-x вместо Basic), либо не отправляйте код ответа 401 при сбое, а отправьте, например, 200 или 400 и добавьте сообщение об ошибке, объясняющее ситуацию.

Комментарии:

1. Для Q3, когда вы говорите or don't send the response code 401 on failure but send i.e. 200 or 400 and add an error message explaining the situation. , как вы это делаете? Похоже, вы подразумеваете, что можете сделать это без изменения заголовка… Не могли бы вы показать пример?

2. Вы можете создать свое собственное пользовательское исключение и указать код состояния в качестве второго параметра или создать встроенное исключение

3. Я обновил свой код CakePHP до 2.5.2, но когда я выполняю приведенный выше JS-код, я вижу приглашение для входа в систему из браузера… таким образом, как будто он запрашивает аутентификацию, чтобы иметь возможность фактически отправить запрос через AJAX, если это имеет смысл? Несмотря на то, что заголовки задаются в опции beforeSend! Можете ли вы увидеть какую-либо причину, почему? Имя пользователя и пароль указаны правильно, как будто я ввожу их в приглашение, и тогда это работает! Но даже если они были неверными, я не хочу видеть приглашение и вместо этого показываю ошибку только в моем JS…

4. Я не знаю, верна ли ваша функция base64, я бы использовал внутреннюю функцию btoa() для ее кодирования, которая работает. И если вы не хотите, чтобы приглашение читало то, что я объясняю в Q3, где я говорю о заголовках, отправляемых с сервера.

5. Пробовал, но заголовки никогда не отправляются (просматриваются в веб-инспекторе), потому что у него нет доступа для их отправки, поскольку он еще не авторизован… и если я введу свои данные в приглашение, а затем обновлю страницу с включенным JS, тогда заголовки будут отправлены JS … почему это происходит? Я думал, что beforeSend обработал это, чтобы отправить заголовки ПЕРЕД запросом?