За последние 24 часа нас посетил 62191 программист и 1791 робот. Сейчас ищут 855 программистов ...

Реализация класса авторизации

Тема в разделе "PHP для новичков", создана пользователем MyZik, 1 янв 2016.

  1. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Доброго времени суток. Недавно начал изучать ООП, поэтому пытаюсь написать для себя более-менее рабочий движок. Застрял с написанием ядра. После перезапуска браузера, пользователь вновь не авторизован. Хотя куки и сессии вроде как прописаны правильно.

    Код основного ядра системы /includes/core.php:
    Код (PHP):
    1. <?php
    2. /**
    3.  * @package SkyBlog CMS
    4.  * @author MyZik
    5.  * @link http://skyblogcms.ru
    6.  */
    7.  
    8. session_name('sid');
    9.  
    10. /**
    11.  * Устанавливаем системные настройки
    12.  */
    13. ini_set("display_errors","1"); // Показ ошибок
    14. ini_set("display_startup_errors","1");
    15. ini_set('error_reporting', E_ALL);
    16. mb_internal_encoding('UTF-8'); // Кодировка по умолчанию
    17.  
    18. /**
    19.  * Системные переменные и константы
    20.  */
    21. define('HOME', dirname(__DIR__)); // Серверный путь к сайту
    22. define('URL', 'http://' . $_SERVER['HTTP_HOST']); // URL-путь к сайту
    23. define('SkyBlog_VERSION', '1.0.0'); // Версия движка (!НЕ МЕНЯТЬ!)
    24.  
    25. /**
    26.  * Инициализация PDO, подключение к БД
    27.  */
    28. $mySQL = parse_ini_file(HOME . '/includes/database.ini');
    29. $db = new PDO('mysql:host=' . $mySQL['host'] .';dbname=' . $mySQL['base'], $mySQL['user'], $mySQL['password'], array(
    30.         PDO::ATTR_PERSISTENT => true
    31. )) or die('Cannot connect to MySQL server :(');
    32. $db->query("SET NAMES utf8");
    33.  
    34.  
    35. /**
    36.  * Загружаем классы
    37.  */
    38. spl_autoload_register(function($name) {
    39.     $file = dirname(__DIR__) . '/includes/classes/' . $name . '.php';
    40.     if(!file_exists($file)) {
    41.         throw new Exception('Autoload class: File '.$file.' not found');
    42.     }
    43.  
    44.     require $file;
    45. });
    46.  
    47. $user = new User;
    48. $userID = $user->userID;
    Теперь, код класса User: /includes/classes/User.php
    Код (PHP):
    1. <?php
    2. /**
    3.  * @package SkyBlog CMS
    4.  * @author MyZik
    5.  * @link http://skyblogcms.ru
    6.  */
    7.  
    8. class User {
    9.     public $user;
    10.     public $userID, $login, $password, $email;
    11.     
    12.     function __construct() {
    13.         global $db;
    14.         
    15.         if (isset($_SESSION['user_id']) && $db->query("SELECT COUNT(*) FROM `users` WHERE `id` = '" . $_SESSION['user_id'] . "' LIMIT 1")->fetchColumn() == 1) {
    16.             # Создаем массив с данными пользователя
    17.             $this->user = $db->query("SELECT * FROM `users` WHERE `id` = '" . $_SESSION['user_id'] . "' LIMIT 1")->fetch();
    18.             
    19.             setcookie('user_id', $user['id'], time() + 60 * 60 * 24 * 365);
    20.             setcookie('password', md5($user['password']), time() + 60 * 60 * 24 * 365);
    21.             
    22.             # Записываем дату последнего посещения
    23.             $db->query("UPDATE `users` SET `last_time_entry` = '" . time() . "' WHERE `id` = '" . $this->user['id'] . "' LIMIT 1");
    24.             
    25.             $this->userID = $this->user['id'];
    26.         }
    27.     }
    28.     
    29.     public static function login($login, $password) {
    30.         global $db;
    31.     
    32.         $user = $db->query("SELECT * FROM `users` WHERE `login` = '$login' AND `password` = '" . md5($password) . "' LIMIT 1")->fetch(); // Создаем массив с данными юзера  
    33.         
    34.         $_SESSION['user_id'] = $user['id']; // Создаем ID в сессиии
    35.     
    36.         $db->query("UPDATE `users` SET `last_entry_time` = '" . time() . "' WHERE `id` = '" . $user['id'] . "' LIMIT 1"); // Обновляем дату последнего посещения
    37.     }
    38.     
    39.     public function registerUser($user_login, $user_password, $user_email) {
    40.         global $db;
    41.         
    42.         $this->login = $user_login;
    43.         $this->password = md5($user_password);
    44.         $this->email = $user_email;
    45.         
    46.         /**
    47.          * Подготавливаем запрос
    48.          */
    49.         $st = $db->prepare("INSERT INTO `users` (`login`, `password`, `email`, `time`)
    50.                 VALUES (:login, :password, :email, :time)");
    51.         /**
    52.          * Регистрируем пользователя в БД
    53.          */
    54.         $st->execute(array(
    55.                 'login' => $this->login,
    56.                 'password' => $this->password,
    57.                 'email' => $this->email,
    58.                 'time' => time()
    59.         ));
    60.         
    61.         return true;
    62.     }
    63. }
    Ну, и простейшая форма авторизации:
    Код (PHP):
    1. <?php
    2. /**
    3.  * @package SkyBlog CMS
    4.  * @author MyZik
    5.  * @link http://skyblogcms.ru
    6.  */
    7.  
    8. require_once '../includes/core.php';
    9.  
    10. $title = 'Авторизация';
    11. require_once '../includes/header.php';
    12.  
    13. if (isset($_POST['auth'])) {
    14.     if ($db->query("SELECT COUNT(*) FROM `users` WHERE `login` = '" . $_POST['login'] . "' AND `password` = '" . md5($_POST['password']) . "' LIMIT 1")->fetchColumn() == 1) {
    15.         echo 'Authorized!';
    16.         $user->login($_POST['login'], $_POST['password']);
    17.     } else {
    18.         echo 'Error';
    19.     }
    20. }
    21. ?>
    22.  
    23. <div class="list-group">
    24.     <div class="list-group-item">
    25.     <form class="form-horizontal" action="entry.php" method="POST" role="form">
    26.         <div class="form-group">
    27.             <label for="login" class="col-sm-1 control-label">Логин</label>
    28.             <div class="col-sm-10">
    29.                 <input type="text" class="form-control" name="login" placeholder="Login">
    30.             </div>
    31.         </div>
    32.          <div class="form-group">
    33.             <label for="password" class="col-sm-1 control-label">Пароль</label>
    34.             <div class="col-sm-10">
    35.                  <input type="password" class="form-control" name="password" placeholder="Password">
    36.             </div>
    37.         </div>
    38.         <div class="form-group">
    39.             <div class="col-sm-offset-1 col-sm-10">
    40.                 <input type="submit" name="auth" class="btn btn-primary btn-lg btn-block" value="Войти!" />
    41.             </div>
    42.         </div>
    43.     </form>
    44.     </div>
    45. </div>
    46.  
    47. <?php
    48. require_once '../includes/footer.php';
    Заранее спасибо за ответ.

    Выделение цветом синтаксиса работает если сделать [code=php]
          — модераторъ
     
  2. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    Сессионная кука по умолчанию живет до закрытия браузера. И лучше оставить это время как есть, иначе получишь новые проблемы!

    Хочешь запомнить юзера на бОльший срок — используй для идентификации не сессию, а отдельную куку для которой задавай время какое хочешь.

    Добавлено спустя 3 минуты 21 секунду:
    По коду есть замечания:
    — раз уж ты используешь классы, избавься от global, передавай $db в конструктор класса User и сохраняй в приватном свойстве.
    — вот это говнокод:
    Код (PHP):
    1.         if (isset($_SESSION['user_id']) …) {
    2.             …
    3.             setcookie('user_id', $user['id'], time() + 60 * 60 * 24 * 365);
    4.             setcookie('password', md5($user['password']), time() + 60 * 60 * 24 * 365);
    5.  
    сам догадаешся почему?
     
  3. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Хм. Наверное, задаю COOKIE и нигде их не использую? Или, может быть, в куках нельзя хранить пароль?
    Да и вообще, код весь мне самому не нравится, но ничего с этим поделать не могу. Например, при авторизации и переходу по сайту - ошибка headers already sent by. Если перенести setcookie чуть выше в __construct - тоже самое, только уже раз в 5 минут приблизительно.
    Уже и не знаю что делать...
     
  4. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Про эту ошибку столько всего написано....
     
  5. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Я в курсе. И кидал код выше, в самый верх, и лишних пробелов нигде нет.
     
  6. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    Наверное потому говнокод, что ты заносишь в куку то, что тебе уже известно из сессии. Зачем этот дубль?
    Перепиши с нуля. Помоему здесь не за что держаться.
     
  7. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Хм. Разве при написании не нужно работать и с $_SESSION и с $_COOKIE? Ну, т.е., чтобы данные в них не различались.
    Попробую переписать заново, да только я не знаю уже, как.
     
  8. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    ИЛИ, а не И

    Добавлено спустя 53 секунды:
    Идентификатор пользователя или в сессионной переменной или в специальной куке. Нет смысла читать значение из сессии и класть его в куку.

    Немного теории: viewtopic.php?f=13&t=53234&p=426001#p426001
     
  9. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Понял, спасибо. Попробую что-нибудь состряпать и напишу сюда. Пока никак не получается подружиться с PHPStorm.
     
  10. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Ещё бывает коварный BOM
     
  11. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    В общем, переписал авторизацию на COOKIE. Хранятся ID пользователя + хэш. Авторизация вроде как работает без ошибок, всё хорошо.
    Но теперь запутался. Дело в том, что не удаляются COOKIE, как бы не пытался.

    class User
    Код (PHP):
    1. <?php
    2. class User {
    3.     public $user;
    4.     public $userID, $login, $password, $email;
    5.  
    6.     function __construct() {
    7.         global $db;
    8.  
    9.         /**
    10.          * Проверяем наличие COOKIE пользователя
    11.          */
    12.         if (isset($_COOKIE['id']) && isset($_COOKIE['hash']) && (!empty($_COOKIE['id']) && !empty($_COOKIE['hash']))) {
    13.             /**
    14.              * Создаем массив с данными юзера
    15.              */
    16.             $req = $db->query("SELECT * FROM `users` WHERE `id` = '" . intval($_COOKIE['id']) . "' LIMIT 1");
    17.             $this->user = $req->fetch();
    18.             $this->userID = $this->user['id'];
    19.  
    20.             /**
    21.              * Если COOKIE не верны, обнуляем
    22.              */
    23.             if (($this->user['hash'] !== $_COOKIE['hash']) || ($this->userID !== $_COOKIE['id'])) {
    24.                 setcookie("id", "", time() - 3600 * 24 * 30 * 12, "/");
    25.                 setcookie("hash", "", time() - 3600 * 24 * 30 * 12, "/");
    26.             }
    27.         }
    28.     }
    29.  
    30.     public function registerUser($user_login, $user_password, $user_email) {
    31.         global $db;
    32.  
    33.         $this->login = $user_login;
    34.         $this->password = md5($user_password);
    35.         $this->email = $user_email;
    36.  
    37.         /**
    38.          * Подготавливаем запрос
    39.          */
    40.         $st = $db->prepare("INSERT INTO `users` (`login`, `password`, `email`, `time`)
    41.                 VALUES (:login, :password, :email, :time)");
    42.         /**
    43.          * Регистрируем пользователя в БД
    44.          */
    45.         $st->execute(array(
    46.             'login' => $this->login,
    47.             'password' => $this->password,
    48.             'email' => $this->email,
    49.             'time' => time(),
    50.             'last_entry_time' => time()
    51.         ));
    52.  
    53.         return true;
    54.     }
    55.  
    56.     public static function login($login, $password) {
    57.         global $db;
    58.  
    59.         $query = $db->query("SELECT * FROM `users` WHERE `login` = '$login' AND `password` = '$password' LIMIT 1");
    60.         $user = $query->fetch();
    61.  
    62.         // Generate random string
    63.         $hash = md5(Core::generateCode(10));
    64.  
    65.         /**
    66.          * Подготавливаем запрос
    67.          */
    68.         $st = $db->prepare("UPDATE `users` SET `hash` = :hash, WHERE `id` = :id");
    69.         /**
    70.          * Обновляем данные в БД
    71.          */
    72.         $st->execute(array(
    73.             'hash' => $hash,
    74.             'id' => $user['id']
    75.         ));
    76.  
    77.  
    78.         /**
    79.          * Устанавливаем COOKIE
    80.          */
    81.         setcookie("id", $user['id'], time()+60*60*24*30);
    82.         setcookie("hash", $hash, time()+60*60*24*30);
    83.     }
    84. } 
    COOKIE удаляю так:
    Код (PHP):
    1. setcookie("id", "", time() - 3600*24*30*12, "/");
    2.         setcookie("hash", "", time() - 3600*24*30*12, "/");
    В чём проблема?
     
  12. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    отлаживайся. phpfaq.ru/debug
    может быть это место вообще не вызывается или вызывается в неподходящий момент.

    Добавлено спустя 3 минуты 29 секунд:
    когда дело доходит до отладки, не надо думать и рассуждать, очевидно что логика тебя уже подвела. просто проверяй всё подряд.

    Добавлено спустя 1 минуту 53 секунды:
    убери пожалуйста свои авторские ссылки из кода ))) это смешно.
     
  13. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Нет, ничего не получается. Да и авторизация толком не работает. На страницах выходе / авторизации пользователь есть, на главной странице авторизация сбивается. $_COOKIE и $user - пусты, если проверять var_dump`ом.
     
  14. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    В смысле не получается. Покажи как ты отлаживаешь.
     
  15. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Проверял $_COOKIE и $user на всех страницах. Проверял наличие хэша. Хэш в БД не передавался, из-за опечатки, исправил, но в принципе, основная проблема осталась. На главной слетает авторизация, на странице выхода куки и $user не обнуляются.
     
  16. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    MyZik, тебе надо проследить всю цепочку вызовов. Например примерно ты знаешь какой класс какая функция должны вызываться.
    И там самое простое можно написать так: echo "отладка функции testPHP()";
    потом смотришь в окно браузера.
    А по поводу сессий. делай var_dump() на определенных участках кода, которые должны вызваться, и ты увидишь где именно происходит их обнуление.
     
  17. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Да делал уже, всё что можно...
    Почему-то на страницах, которые находятся в корне - авторизация сбивается. В файлах, которые в папках (например /users/) - авторизация работает. Бред какой-то.
     
  18. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    Ты ж не показываешь как ты делаешь. Может что тебе нужно не подключается.
     
  19. MyZik

    MyZik Новичок

    С нами с:
    6 июн 2015
    Сообщения:
    13
    Симпатии:
    0
    Господи, получилось наконец-то. Просто до слёз. Я даже как сделал не понял.
     
  20. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    Для опытных товарищей это очевидно, но не для нубов:
    1. когда делаешь setcookie('name'…), значение $_COOKIE['name'] не меняется! Новое значение будет прочитано в переменную только при следующем запросе.
    2. если ты в процессе жизнедеятельности успел насоздавать кук с разными значениями path, ты будешь получать разные значения одной и той же куки на разных страничках — депрессия гарантирована.
    разный path появляется если этот параметр не указан явно при вызове setcookie, считается что кука ставится для текущей папки (и вложенных в неё).

    Перед началом отладки чисти все куки домена!