четверг, 23 августа 2012 г.

Basic authorization logout

Как разлогиниться, если используешь Basic-авторизацию или, по-другому, HTTP-авторизацию?
Как оказалось не так уж просто, но все же возможно, причем вся логика - на серверной стороне, а значит метод кроссбраузерный!

Все что потребуется - контролировать пару флагов в сессии.
Суть в том, что если юзер авторизован - в сессии хранится его ID, а раз уж ID есть, то и пароль с логином спрашивать ни к чему. Однако есть и другой флаг, назовем его reauth, который показывает, что мы затребовали выход из системы.
При взведении этого флага сессия юзера очищается, а логин-пароль принудительно прячутся (т.е. метод getEnvLoginPasswd() просто говорит "а нету, пытай меня сволочь немецкая!"), это важно, иначе чуда не произойдет.



В итоге имеем такой класс:

class auth {
        protected $suid = null; // current session user id
        protected $suid_var = 'sess_uid';
        protected $reauth_var = 'need_reauth';
        public function __construct() {
                session_start();
                $this->suid = @$_SESSION[$this->suid_var];
        }
        public function check() {
                if (!$this->isAuthorized()) {
                        return $this->requestAuth();
                }
                return $this->getSessionID();
        }
        public function isAuthorized() {
                return ($this->getSessionID() or $this->isAccessGranted())
                    and empty($_SESSION[$this->reauth_var]);
        }
        protected function getSessionID() {
                return $this->suid;
        }
        protected function isAccessGranted() {
                if (!(list($login, $pass) = $this->getEnvLoginPasswd())) {
                        return false;
                }
                return $_SESSION[$this->suid_var] = $this->suid = $this->verifyCredentials($login, $pass);
        }
        protected function getEnvLoginPasswd() {
                if (!empty($_SESSION[$this->reauth_var])) {
                        return false;
                }
                if (empty($_SERVER['PHP_AUTH_USER']) or empty($_SERVER['PHP_AUTH_PW'])) {
                        return false;
                }
                return array($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
        }
        protected function verifyCredentials($login, $pass) {
                return $login == 'log' and $pass == 'pass';
        }
        public function logout() {
                $_SESSION[$this->reauth_var] = true;
                unset($_SESSION[$this->suid_var]);
                $this->suid = null;
                header('Location: /');
        }
        public function requestAuth() {
                unset($_SESSION[$this->reauth_var]);
                header('WWW-Authenticate: Basic realm="Hallou"');
                header('HTTP/1.0 401 Unauthorized');
                echo 'Требуется авторизация';
                echo '<script>document.execCommand("ClearAuthenticationCache");</script>';
                exit;
        }
}
В методе verifyCredentials происходит проверка введенных данных. Имена методов вообще говорят сами за себя. Пользоваться классом просто:
$a = new auth;
$a->check();
if ($_SERVER['QUERY_STRING'] == 'logout') {
    $a->logout();
}
Метод должен был быть кроссбраузерным, но оказалось, что он не работает в опере и в ИЕ..
Chrome и FF более охотно "забывают" данные для входа, поэтому с ними возни меньше.
Чтобы сбросить кэш авторизации в ИЕ добавлена строка со скриптом в метод requestAuth();
Осталась только вредная опера.. может как нить руки дойдут допилить

P.S. В качестве бонуса данный метод позволяет ввести новую пару логин/пароль сразу, после разлогинивания, потому что перенаправляет нас с logout-экшена на главную.

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

Отправить комментарий