CakePHP 2.3.2 BasicAuthentication не работает

Я попробовал "Простое приложение 1 и 2, управляемое Acl", расположенное по адресу http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/simple-acl.-controlled-application.html.

После этого я попытался активировать BasicAuth вместо FormAuth.

Я повторно реализовал функцию login () в моем UsersController следующим образом:

public function login() {
if ($this->Auth->login()) {
        return $this->redirect($this->Auth->redirect());
    } else {
        $this->Session->setFlash('Not able to login');
    }
}

и изменил переменную $ components в моем AppController на следующее:

public $components = array(
    'Acl',
    'Auth' => array(
        'authorize' => array(
            'Actions' => array('actionPath' => 'controllers')
        ),
        'authenticate' => array('Basic')
    ),
    'DebugKit.Toolbar',
    'Session'
);

Всплывающее окно BasicAuth появляется, как и ожидалось, но когда я пытаюсь войти в систему, оно повторяется в бесконечном цикле. Я ничего не менял после прохождения учебника, кроме включения DebugKit.

Что мне не хватает? Я надеюсь, что кто-то может мне помочь, так как я хотел бы написать код CakePHP для моего следующего проекта!

Обновить

AppController

public function beforeFilter() {
    //Configure AuthComponent
    $this->Auth->allow('display');
    $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
    $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
    $this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'add');
}

UsersController

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

Я пытаюсь получить доступ, например, /users/, который работает как чудо с использованием FormAuth, описанного в руководстве, поэтому проблем с разрешением быть не может. Logindata довольно проста для тестирования (admin: admin), поэтому проблем возникнуть не должно.

Обновление 2

В моем журнале Apache я получаю следующее, значит, я не авторизован:

IP - - [16 / апр / 2013: 18: 08: 37 +0200] "GET / users / login HTTP / 1.0" 401 5179 "-" "Mozilla / 5.0 (Windows NT 6.2; Win64; x64; rv: 23.0) Gecko / 20130414 Firefox / 23,0 "

Обновление 3

Почему-то кажется, что Пользователь и Пароль либо не отправляются, либо не сохраняются в PHP. Если я перепишу /lif/Cake/Controller/Auth/BasicAuthenticate на следующий, все заработает!

public function authenticate(CakeRequest $request, CakeResponse $response) {
    $_SERVER['PHP_AUTH_USER'] = $_SERVER['PHP_AUTH_PW'] = "admin";
    $result = $this->getUser($request);

    if (empty($result)) {
        $response->header($this->loginHeaders());
        $response->statusCode(401);
        $response->send();
        return false;
    }
    return $result;
}

Обновление 4

Не знаю, полезно ли это, но на сервере работает Plesk 11, последнее обновление, никаких особых изменений.

Обновление 5

Хорошо, этот ответ «thaJeztah» был полезен, но теперь у меня появляется больше проблем, которые можно подразделить.

  1. Изменен режим с fcgid на модуль apache

    1.1. Приводится к рабочему входу в систему, но выход из системы не работает! После перенаправления сеанс кажется очищенным, но я все еще могу получить доступ к каждой закрытой странице, пока я не отключу в своих браузерах «Активный вход», как это называется в Firefox.

var_dump($this->Session->read('Auth.User'));

NULL

Когда я получаю доступ к / users / login, я автоматически вхожу в систему и перенаправляюсь без необходимости вводить учетные данные для входа.

print "<pre>";
print_r($this->Session->read('Auth.User'));
print "</pre>";

Array
(
    [id] => 1
    [username] => admin
    [group_id] => 1
    [created] => 2013-04-12 12:54:26
    [modified] => 2013-04-16 14:27:24
    [is_active] => 1
    [Group] => Array
        (
            [id] => 1
            [name] => Admin
            [created] => 2013-04-12 12:46:42
            [modified] => 2013-04-12 12:46:42
        )

)
  1. Использование решения на основе .htaccess тоже работает, даже похоже, что это единственное необходимое изменение (я удалил код list (), так как я никогда не входил в него, и он тоже сработал).

    2.1. Та же проблема, что и выше, реальный выход из системы невозможен.

Обновление 6

Наверное, последнее или одно из последних моих обновлений. :-) Прямо сейчас я пытаюсь выполнить «фальшивый выход», зарегистрировав пользователя в качестве гостя, созданного мной, у которого есть доступ только к /users/login и /pages/home: http://guest:[email protected]/users/login

Доступ к /users/logout тоже может сработать, поскольку я использую здесь этот фрагмент кода:

public function logout() {
    $user = $this->User->find('first', array('conditions' => array('username' => 'guest')));
    $this->Auth->login($user['User']['id']);
}

Я просто не верю, что это будет согласованно, поскольку я считаю, что данные сеанса будут удалены через некоторое время, а браузер все еще получит активный логин администратора и аутентифицируется с их помощью - я прав?

После этого я могу снова войти в систему с другим пользователем, используя http://admin:[email protected]/users/login. Не идеально, но работает по крайней мере для Firefox.

Итак, в основном последний вопрос: есть ли предложения о том, как принудительно использовать BasicAuth при доступе к /users/login? Таким образом, я мог легко переключать пользователей в любое время с любого клиента.

Обновление 7

Я нашел способ сделать именно это с помощью идеи в моем принятом ответе. Надеюсь, я уловил все крайние случаи в этом, не стесняйтесь поправлять меня, если нет!

(Ps: при использовании ACL и / или базовой аутентификации isAuthorized (), по крайней мере, в AppController, кажется, игнорируется (он был распознан, но не повлиял - когда я удалил метод без изменения $ components, я получил ошибку), что привело мне реализовать это без использования isAuthorized ().)

AppController.php

public function beforeFilter($redirectlogin = true) {
    //Configure AuthComponent
    $this->Auth->allow('display', '/users/login');
    $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
    $this->Auth->logoutRedirect = array('controller' => 'pages', 'action' => 'home');
    $this->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'home');
    $this->Auth->unauthorizedRedirect = array('controller' => 'HTTPCODE', 'action' => 'c403');

    if($redirectlogin && $this->Session->read('Auth.needs_reauthenticate')) {
        if(!($this->request->params['controller'] == $this->Auth->loginRedirect['controller'] && $this->request->params['pass'][0] == $this->Auth->loginRedirect['action'])) {
            $this->redirect('/users/login');
        }
    }
 }

UsersController.php

public function beforeFilter() {
    parent::beforeFilter(false);
}

    public function login() {
        $this->autoRender = false;
        $this->Session->write('Auth.needs_reauthenticate', true);
        if(!$this->Session->check('Auth.count')) {
            $count = 1;
        } else {
            $count = $this->Session->read('Auth.count') + 1;
        }
        $this->Session->write('Auth.count', $count);

        if($this->Session->read('Auth.needs_reauthenticate')) {
            if((isset($_SERVER['HTTP_AUTHORIZATION']) && $this->Session->read('Auth.count') == 1) || (!isset($_SERVER['HTTP_AUTHORIZATION']) || empty($_SERVER['HTTP_AUTHORIZATION']) || !$this->Session->check('Auth.sent_header_step') || $this->Session->read('Auth.sent_header_step') < 1)) {
                unset($_SERVER['HTTP_AUTHORIZATION']);
                $this->Session->write('Auth.redirectTo', $this->Auth->redirect());

                $this->response->header(sprintf('WWW-Authenticate: Basic realm="%s"', env('SERVER_NAME')));
                $this->response->statusCode(401);
                $this->response->send();

                $this->Session->write('Auth.sent_header_step', 1);
            }       

            if(isset($_SERVER['HTTP_AUTHORIZATION'])) {
                $this->Session->write('Auth.sent_header_step', 0);
                $base64string = base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6));
                if(!(strlen($base64string) > 1 && substr($base64string, -1, 1) != ":")) {
                    $_SERVER['PHP_AUTH_USER'] = "";
                    $_SERVER['PHP_AUTH_PW'] = "";
                }

                $data = true;
            }

            $this->Auth->logout();

            if(isset($data) && $this->Session->read('Auth.count') > 1) {
                if($this->Auth->login()) {
                    $this->Session->write('Auth.needs_reauthenticate', false);
                    if($this->Session->check('Auth.redirectTo')) {
                        $redirectTo = $this->Session->read('Auth.redirectTo');
                        $this->Session->delete('Auth.redirectTo');
                        $this->Session->delete('Auth.count');

                        return $this->redirect($redirectTo);
                    } else {
                        return $this->redirect($this->Auth->redirect());
                    }
                } else {
                    $this->response->statusCode(403);
                    // my 403 message
                }
            } else {

                if(!isset($_SERVER['HTTP_AUTHORIZATION']) && $this->Session->read('Auth.count') > 1 && isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) && trim($_SERVER['PHP_AUTH_USER']) != "" && trim($_SERVER['PHP_AUTH_PW']) != "") {
                    if($this->Auth->login()) {
                        $this->Session->write('Auth.needs_reauthenticate', false);
                        if($this->Session->check('Auth.redirectTo')) {
                            $redirectTo = $this->Session->read('Auth.redirectTo');
                            $this->Session->delete('Auth.redirectTo');
                            $this->Session->delete('Auth.count');

                            unset($_SERVER['HTTP_AUTHORIZATION']);
                            unset($_SERVER['PHP_AUTH_USER']);
                            unset($_SERVER['PHP_AUTH_PW']);
                            return $this->redirect($redirectTo);
                        } else {
                            return $this->redirect($this->Auth->redirect());
                        }
                    } else {
                        $this->response->statusCode(403);
                        // my 403 message
                    }
                }

                $this->response->statusCode(403);
                // my 403 message
            }
        }
    }

заранее спасибо

Адриан


person boindil    schedule 16.04.2013    source источник
comment
Вы добавляли функции в beforeFilter (как в учебнике)? В частности $this->Auth->loginRedirect. Похоже, вы вошли в систему, но перенаправление работает некорректно. Если у вас есть этот код, можете ли вы обновить свой вопрос с его помощью? И проверьте, действительно ли авторизованный пользователь имеет разрешение на доступ к этому действию?   -  person Nunser    schedule 16.04.2013
comment
@nuns: Обновил свой пост запрошенной информацией   -  person boindil    schedule 16.04.2013
comment
Вы используете cakephp 2.3, попробуйте изменить return $this->redirect($this->Auth->redirect()); на return $this->redirect($this->Auth->redirectUrl()); в функции входа в систему (и верните BasicAuthenticate в исходное состояние). Если это не сработает, поскольку перезапись сработала для вас, я бы порекомендовал расширить класс BasicAuthenticate и изменить функцию getUser (). Я видел пару сообщений с похожими проблемами, и, похоже, решение.   -  person Nunser    schedule 16.04.2013
comment
Если подумать ... если вам нужно установить переменные env PHP_AUTH_USER и PHP_AUTH_PW вручную, это означает, что они не были установлены раньше (да). Где вы их ставите? Какой у вас код для вызова http rquest?   -  person Nunser    schedule 16.04.2013
comment
@nuns обновил мой пост на основе ответа thaJeztah, он включает эту мысль (Обновление 5)   -  person boindil    schedule 17.04.2013


Ответы (1)


Использование базовой аутентификации при запуске PHP как (Fast) CGI

Возможно, ваш веб-сайт настроен на запуск PHP как (Fast) CGI, и в этом случае ключи PHP_AUTH_USER и PHP_AUTH_PWD отсутствуют в переменной $_SERVER. AuthComponent BasicAuthenticate полагается на эти ключи.

Либо измените настройки домена / веб-хостинга в Plesk, чтобы запускать php как «модуль apache» для этого веб-сайта / домена, либо расширьте компонент BasicAuthenticate, чтобы получить эти переменные другим способом.

Более подробную информацию по этому вопросу можно найти в этом вопросе:

PHP_AUTH_USER не установлен?

И для фреймворка Symfony кто-то, кажется, написал обходной путь, который также может быть полезен в этой ситуации;

https://github.com/symfony/symfony/issues/1813

обновление: выход из системы при использовании базовой аутентификации

Выход из системы при использовании basic authentication на самом деле невозможен. Обычная проверка подлинности - это механизм проверки подлинности без сохранения состояния, что в основном означает, что браузер отправляет учетные данные пользователя с каждым запросом. Другими словами; сервер не сохраняет "состояние", в отличие от браузера. При базовой аутентификации вам необходимо, чтобы браузер отправлял учетные данные пользователя, и пока браузер отправляет действительные учетные данные, вы разрешаете браузеру доступ к защищенным страницам.

Единственный способ выйти из системы - это закрыть браузер или указать браузеру закрыть активные сеансы / входы.

Подробнее читайте здесь:

http://en.wikipedia.org/wiki/Basic_access_authentication

https://stackoverflow.com/questions/4163122/http-basic-authentication-log-out

Примечания

Базовая аутентификация не является безопасным механизмом аутентификации; имя пользователя и пароль отправляется на сервер с каждым запросом. Пароль отправляется в незашифрованном виде (только в кодировке base64 для предотвращения проблем со специальными символами).

Хотя проверка подлинности с помощью формы также отправляет пароль в незашифрованном виде, он (немного более) безопасен, поскольку он отправляет имя пользователя / пароль только при входе в систему. Последующие запросы будут отправлять только идентификатор сеанса, который может быть установлен на срок действия и ограничен конкретный IP-адрес и / или тип браузера.

Во всех случаях, очевидно, важна защита соединения через SSL.

Принудительная повторная аутентификация на странице входа

Это просто «мысли вслух», непроверенные и в высшей степени экспериментальные :)

Попробуй это;

  1. Если ни один сеанс не активен, действуйте обычным образом. Невозможно отличить «уже вошедших в систему» ​​пользователей от «новых пользователей» в базовой аутентификации - она ​​не имеет состояния.

  2. Если сеанс активен, очевидно, что у пользователя активный сеанс. Не уничтожать сеанс, но измените правила;

    • If the credentials sent are for the same user as the username inside the session (or, better: $this->Auth->user('username');?, Then invalidate the session (not destroy) and force the user to re-authenticate, by sending login headers;
    • Вы можете скопировать заголовки из поведения BasicAuthenticate; см. источник здесь BasicAuthenticate :: Authenticate ()
    • По поводу «копирования заголовков»; Возможно, расширение BasicAuthenticate - более чистый подход; обрабатывать весь свой собственный код внутри настроенной версии.

Кроме того, проверьте, является ли сеанс «действительным» внутри AppController::isAuthorized() (см. Использование ControllerAuthorize)

Примерно так (код мокапа):

Страница входа / действие:

if ("usercredentials sent by browser" === "current logged in user in session") {
    // Mark session as 'needs-to-reauthenticate'
    $this->Session->write('Auth.needs_reauthenticate', true);

    // Need to find a clean approach to get the BasicAuth loginHeaders()
    // *including* the right settings (realm)
    $this->response->header(/*BasicAuth::loginHeaders()*/);

    // Access denied status
    $this->response->statusCode(401);
    return $this->response->send();
}

AppController :: isAuthorized ()

if ($this->Session->read('Auth.needs_reauthenticate')) {
    return false;
} else {
    // Normal 'isAuthorized()' checks here
}

ПРИМЕЧАНИЕ. После того как браузер посетил страницу «входа» во время активного сеанса, пользователь должен будет либо войти в систему с другими учетными данными, либо закрыть браузер для входа. снова.

Это может быть проблемой, если файл cookie сеанса все еще присутствует после закрытия и повторного открытия браузера. Попытайтесь сделать файл cookie сеанса "настоящим" файлом cookie сеанса и удалите его при закрытии браузера, установив для Session.cookieTimeout значение 0 (см. _ 11_

person thaJeztah    schedule 16.04.2013
comment
@boindil добавил некоторую информацию к моему ответу, чтобы объяснить механизм базовой аутентификации. - person thaJeztah; 17.04.2013
comment
Спасибо большое, это мне очень помогло! Мне это нужно, потому что я пишу REST API и пытаюсь сделать его безопасным и гибким. Однако мне нужно использовать BasicAuth, поскольку это руководство (сам API является переписанным, и есть несколько клиентов, которые нельзя легко изменить). Да, конечно, SSL будет использоваться. Обновил свой пост (Обновление 6). - person boindil; 17.04.2013
comment
@boindil Я немного подумал, добавил это к своему ответу. Непроверенный, просто думаю вслух: D - person thaJeztah; 17.04.2013
comment
Еще раз спасибо за вашу помощь, я отредактировал решение, которое работает для меня, в обновлении 7. - person boindil; 20.04.2013
comment
Рад, что у вас все получилось! Чтобы улучшить ваш код, я бы предложил переместить часть этой логики из вашего контроллера и (например) в настраиваемый объект аутентификации (см. Создание пользовательских объектов аутентификации). Однако вопрос не в этом. Опять же, отлично, у вас все заработало! - person thaJeztah; 20.04.2013