Авторизация на сессиях и куках, PHP+MySQL v.2

Сентябрь 19th, 2013 Рубрики: PHP coubertin.cz www.colvillewoodworking.com

Авторизация

Что же, давно просили более расширенный вариант авторизации. Данная авторизация использует классы. Опять оговорюсь что это примерная реализация а не оптимальная. Можно улучшить защиту, добавить разделение прав и много чего еще. Я уже не говорю о таких банальных вещах как рефакторинг. Тем не менее она вполне сносно написана во время очередного night programming.

Пожалуй стоит начать с описании логики.
Как известно время хранения сессии 30 минут. Железно увеличивать ее в настройках сервера не стоит, это повысит нагрузку на сервер и скорее всего приведет к потери производительности. Так же мы можем назначить свою папку для хранения сессий, но тогда нам нужно писать инструмент для ее очистки — иначе мы будем хранить все сессии которые когда либо существовали, а нам этого не нужно. Это опять таки увеличит нагрузку, снизит время отклика, а если вы используете маленький хостинг план то у вас вообще место может закончится, при условии что очистка этой папки была написана с ошибками.

Оптимальный вариант это комбинировать сессии с куками. В первую очередь при обращении к странице проверяется существование сессии. Если сессии нету то мы проверяем наличие куки.

Соответственно если кук нету мы отправляем человека на форму авторизации. Если же они есть мы сравниваем их с записью которая есть в нашей базе. Тут уже много вариантов для увеличения безопасности. Будь то случайно сгенерированный код, user agent, ip пользователя и черт знает еще что. Оговорюсь что проверку на ip в данное время делать не стоит. Дело в том что сейчас очень распространены мобильные устройства, мощные смартфоны и планшеты… Все чаще появляется мобильный трафик.
При чем тут это? Все просто. При использовании мобильного интернета вы не имеете белого ip (я уже не говорю о статическом ip) и каждый раз когда вы заново подключаетесь к сети меняется сервер через который идет ваш трафик (соответственно и ip) да и ваш ip тоже меняется.

Думаю что пора приступать к коду.
Для начала создадим две таблички в MySQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE TABLE IF NOT EXISTS `session` (
  `id_user` INT(5) NOT NULL,
  `code_sess` VARCHAR(15) NOT NULL,
  `user_agent_sess` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id_user`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `users` (
  `id_user` INT(5) NOT NULL AUTO_INCREMENT,
  `login_user` VARCHAR(60) NOT NULL,
  `passwd_user` VARCHAR(255) NOT NULL,
  `mail_user` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id_user`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Из их названий понятно что первая содержит текущие сессии а вторая данные о пользователях.
Далее естественно conf.php — наш файл конфигурации.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
//~ Старт сессии, файл должен быть сохранен без DOM информации
session_start();

include_once 'module.php';

//~ Параметры подключения к бд
$db_host = 'localhost';
$db_login = ''; //~ логин для подключения
$db_passwd = ''; //~ пароль для подключения
$db_name = ''; //~ Имя таблицы

// подключаемся к бд
$db = new mysql(); //~ Создаем новый объект класса
$db -> connect($db_host, $db_login, $db_passwd, $db_name);
?>

Тут даже комментировать нечего. Прописываем данные для MySQL и выполняем подключение.

Далее наш index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
include_once 'conf.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title></title>
</head>
<body>
<?php
$r='';

$auth = new auth(); //~ Создаем новый объект класса

//~ Авторизация
if (isset($_POST['send'])) {
  if (!$auth->authorization()) {
    $error = $_SESSION['error'];
    unset ($_SESSION['error']);
  }
}

//~ выход
if (isset($_GET['exit'])) $auth->exit_user();

//~ Проверка авторизации
if ($auth->check()) $r.='Добро пожаловать '.$_SESSION['login_user'].'<br/><a href="?exit">Выйти</a>';
else {
  //~ если есть ошибки выводим их и предлагаем восстановить пароль
  if (isset($error)) $r.=$error.'<a href="recovery.php">Восстановить пароль</a><br/>';

  $r.='
  <a href="join.php">Зарегистрироваться</a>
  <form action="" method="post">
    login <input type="text" name="login" value="'
.@$_POST['login'].'" /><br />
    passwd <input type="password" name="passwd" id="" /><br />
    <input type="submit" value="send" name="send" />
  </form>
  '
;
}
  print $r;
?>
</body>
</html>

Что бы все было просто понять я не стал разделять html и php. Тут тоже все просто, вся магия будет потом ^_^.
Первое что мы делаем это подключаем наш конфиг (соответственно уже тогда у нас выполняется подключение к MySQL и старт сессий). Далее мы создаем объект класса auth и проверяем существует ли попытка авторизоваться.
Чуть ниже мы прописываем выход который принимаем через $_GET.

За проверку авторизации у нас служит метод $auth->check(), он возвращает true или false соответственно. Т.е. мы проверяем авторизации и далее уже выводим «добро пожаловать» или форму регистрации.

Переходим к файлу join.php который отвечает за регистрацию пользователей.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
include_once 'conf.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title></title>
</head>
<body>
<?php
$reg = new auth();  //~ Создаем новый объект класса
$form = '
  <a href="join.php">Авторизоваться</a><br />
  <form action="" method="post">
    логин <input type="text" name="login" id="" value="'
.@$_POST['login'].'" /><br />
    пароль <input type="password" name="passwd" id="" /><br />
    повторите пароль <input type="password" name="passwd2" id="" /><br />
    Почта <input type="text" name="mail" value="'
.@$_POST['mail'].'" /><br />
    <input type="submit" value="send" name="send" />
  </form>
  '
;
if (isset($_POST['send'])) {
  if ($reg->reg($_POST['login'], $_POST['passwd'], $_POST['passwd2'], $_POST['mail'])) {
    print '
      <h2>Регистрация успешна.</h2>
      Вы можете войти <a href="index.php">авторизоваться</a>.
    '
;
  } else print $form;
} else print $form;

?>
</body>
</html>

Тут все аналогично тому что было раньше. Проверяем наличие отправки формы, проверяем ответ метода $auth->reg() и предлагаем перейти к авторизации.

Далее файл recovery.php который отвечает за восстановление пароля

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
include_once 'conf.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title></title>
</head>
<body>
<?php
$reg = new auth();  //~ Создаем новый объект класса
$r='';
$form='
  <form action="" method="post">
    логин <input type="text" name="login" id="" value="'
.@$_POST['login'].'" /><br />
    Почта <input type="text" name="mail" value="" /><br />
    <input type="submit" value="send" name="send" />
  </form>
'
;

if (isset($_POST['send'])) {
  //~ запрос на восстановление пароля
  $reply = $reg->recovery_pass($_POST['login'], $_POST['mail']);
  if ($reply=='good') {
    //~ положительный ответ
    $r.='Новый пароль был выслан вам на почту';
  } else {
    //~ ошибка во время восстановления
    $r.=$reply.$form;
  }
} else $r.=$form;
print $r;

?>
</body>
</html>

Тут даже объяснять нечего.

А теперь перейдем к самому большому файлу в котором происходит вся магия — module.php.
Т.к. в нем чуть больше 200 строк то я его приведу кусками поясняя основные моменты.
Начнем с класса mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class mysql {
  ###
  # Подключение к бд
  function connect($db_host, $db_login, $db_passwd, $db_name) {
    mysql_connect($db_host, $db_login, $db_passwd) or die ("MySQL Error: " . mysql_error()); //~ устанавливаем подключение с бд
    mysql_query("set names utf8") or die ("<br>Invalid query: " . mysql_error()); //~ указываем что передаем данные в utf8
    mysql_select_db($db_name) or die ("<br>Invalid query: " . mysql_error()); //~ выбираем базу данных
  }

  ###
  # Запрос к базе и его производные
  function query($query, $type, $num) {
    if ($q=mysql_query($query)) {
      switch ($type) {
        case 'num_row' : return mysql_num_rows($q); break;
        case 'result' : return mysql_result($q, $num); break;
        case 'accos' : return mysql_fetch_assoc($q); break;
        case 'none' : return $q;
        default: return $q;
      }
    } else {
      print 'Mysql error: '.mysql_error();
      return false;
    }
    //~ !!! DANGER !!!
    //~ при переносе в паблик убрать print 'Mysql error: '.mysql_error();
    //~ эта строчка стоит только для отладки и используя ее в паблике можно засветить запросы
  }

  ###
  # экранирование данных
  function screening($data) {
    $data = trim($data); //~ удаление пробелов из начала и конца строки
    return mysql_real_escape_string($data); //~ экранирование символов
  }
}

Этот небольшой класс был написан что бы облегчить работу с MySQL не используя при этом PDO и MySQLi (о них мы поговорим позже).
Он содержит всего три метода, подключение, запросы и экранирование. Метод mysql->query использует три параметра, сам запрос, его тип и количество.
Для простоты можно сделать их необязательными, например так:

1
function query($query, $type=null, $num=null)

Тогда по умолчанию будет возвращаться дескриптор результата запроса (resource, подробно на php.net).

Далее опишу методы класса mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  ###
  # Проверка входных данных при регистрации
  function check_new_user($login, $passwd, $passwd2, $mail) {
    //~ Проверка валидности данных
    if (empty($login) or empty($passwd) or empty($passwd2)) $error[]='Все поля обязательны для заполнения';
    if ($passwd != $passwd2) $error[]='Введенные пароли не совпадают';
    if (strlen($login)<3 or strlen($login)>30) $error[]='Длинна логина должна быть от 3 до 30 символов';
    if (strlen($passwd)<3 or strlen($passwd)>30) $error[]='Длинна пароля должна быть от 3 до 30 символов';
    //~ Валидация почты не используя регулярки http://www.php.net/manual/en/filter.examples.validation.php
    if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) $error[]='Не корректный email';
    //~ Проверяем наличее пользователя с таким именем в бд
    $db = new mysql(); //~ Создаем новый объект класса
    $login = $db->screening($login);
    if ($db->query("SELECT * FROM users WHERE login_user='".$login."';", 'num_row', '')!=0) $error[]='Пользователь с таким именем уже существует';
    if ($db->query("SELECT * FROM users WHERE mail_user='".$mail."';", 'num_row', '')!=0) $error[]='Пользователь с таким email уже существует';

    //~ Возвращаем массив ошибок или положительный ответ
    if (isset($error)) return $error;
    else return 'good';
  }

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  ###
  # Регистрация
  function reg($login, $passwd, $passwd2, $mail) {
    if (($this->check_new_user($login, $passwd, $passwd2, $mail))=='good') {
      $db = new mysql(); //~ Создаем новый объект класса
      $passwd = md5($db->screening($passwd).'lol'); //~ хеш пароля с солью
      $login = $db->screening($login);
      if ($db->query("INSERT INTO `users` (`id_user`, `login_user`, `passwd_user`, `mail_user`) VALUES (NULL, '".$login."', '".$passwd."', '".$mail."');", '', '')) return true;
      else {
        print 'Возникла ошибка при регистрации нового пользователя. Свяжитесь с администрацией';
        return false;
      }
    } else {
      print $this->error_print($this->check_new_user($login, $passwd, $passwd2, $mail));
      return false;
    }
  }

Сам метод регистрации. Проверяем данные на выходе предыдущим методом, делаем хеш пароля с солью и регистрируем пользователя. Кстати вот очевидный пример где нужно сделать рефакторинг — два вызова auth->check_new_user() когда можно обойтись одним.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  ###
  # Проверка авторизации
  function check() {
    if (isset($_SESSION['id_user']) and isset($_SESSION['login_user'])) return true;
    else {
      //~ проверяем наличие кук
      if (isset($_COOKIE['id_user']) and isset($_COOKIE['code_user'])) {
        //~ куки есть - сверяем с таблицей сессий
        $db = new mysql(); //~ создаем новый объект класса
        $id_user=$db->screening($_COOKIE['id_user']);
        $code_user=$db->screening($_COOKIE['code_user']);
        if ($db->query("SELECT * FROM `session` WHERE `id_user`=".$id_user.";", 'num_row', '')==1) {
          //~ Есть запись в таблице сессий, сверяем данные
          $data = $db->query("SELECT * FROM `session` WHERE `id_user`=".$id_user.";", 'accos', '');
          if ($data['code_sess']==$code_user and $data['user_agent_sess']==$_SERVER['HTTP_USER_AGENT']) {
            //~ Данные верны, стартуем сессию
            $_SESSION['id_user']=$id_user;
            $_SESSION['login_user']=$db->query("SELECT login_user FROM `users` WHERE  `id_user` = '".$id_user."';", 'result', 0);
            //~ обновляем куки
            setcookie("id_user", $_SESSION['id_user'], time()+3600*24*14);
            setcookie("code_user", $code_user, time()+3600*24*14);
            return true;
          } else return false; //~ данные в таблице сессий не совпадают с куками
        } else return false; //~ в таблице сессий не найден такой пользователь
      } else return false;
    }
  }

Метод отвечающий за проверку авторизации. Для начала проверяем наличие сессии. Если сессии отсутствуют то мы проверяем куки. Экранируем их содержимое и сверяем их с таблицей сессий. Если все данные совпадают то мы стартуем сессию. Обновляем куки.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  ###
  # Авторизация
  function authorization() {
    $db = new mysql(); //~ создаем новый объект класса
    $login = $db->screening($_POST['login']);
    $passwd = md5($db->screening($_POST['passwd']).'lol'); //~ хеш пароля с солью
    if ($db->query("SELECT * FROM `users` WHERE  `login_user` =  '".$login."' AND  `passwd_user` = '".$passwd."';", 'num_row', '')==1) {
      //~ пользователь найден в бд, логин совпадает с паролем
      $_SESSION['id_user']=$db->query("SELECT * FROM `users` WHERE  `login_user` =  '".$login."' AND  `passwd_user` = '".$passwd."';", 'result', 0);
      $_SESSION['login_user']=$login;
      //~ добавляем/обновляем запись в таблице сессий и ставим куку
      $r_code = $this->generateCode(15);
      if ($db->query("SELECT * FROM `session` WHERE `id_user`=".$_SESSION['id_user'].";", 'num_row', '')==1) {
        //~ запись уже есть - обновляем
        $db->query("UPDATE `session` SET `code_sess` = '".$r_code."', `user_agent_sess` = '".$_SERVER['HTTP_USER_AGENT']."' WHERE `id_user` = ".$_SESSION['id_user'].";", '', '');
      } else {
        //~ записи нету - добавляем
        $db->query("INSERT INTO `session` (`id_user`, `code_sess`, `user_agent_sess`) VALUES ('".$_SESSION['id_user']."', '".$r_code."', '".$_SERVER['HTTP_USER_AGENT']."');", '', '');
      }
      //~ ставим куки на 2 недели
      setcookie("id_user", $_SESSION['id_user'], time()+3600*24*14);
      setcookie("code_user", $r_code, time()+3600*24*14);
      return true;
    } else {
      //~ пользователь не найден в бд, или пароль не соответствует введенному
      if ($db->query("SELECT * FROM  `users` WHERE  `login_user` =  '".$login."';", 'num_row', 0)==1) $error[]='Введен не верный пароль';
      else $error[]='Такой пользователь не существует';
      $_SESSION['error'] = $this->error_print($error);
      return false;
    }
  }

Данный метод отвечает за авторизацию. Мы экранируем введенные данные и проверяем наличие такого пользователя в базе. При успешном поиске мы запускаем сессию, обновляем данные в таблице сессий, обновляем куки. Иначе сообщаем об полученной ошибке.

1
2
3
4
5
6
7
8
9
  ###
  # Выход
  function exit_user() {
    //~ разрушаем сессию, удаляем куки и отправляем на главную
    session_destroy();
    setcookie("id_user", '', time()-3600);
    setcookie("code_user", '', time()-3600);
    header("Location: index.php");
  }

Тут все просто, удаляем сессию, удаляем куки и перенаправляем на страницу авторизации.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  ###
  # Восстановление пароля
  function recovery_pass($login, $mail) {
    $db = new mysql(); //~ создаем новый объект класса
    $login = $db->screening($login);
    $db_inf = $db->query("SELECT * FROM `users` WHERE `login_user`='".$login."';", 'accos', '');
    if ($db->query("SELECT * FROM `users` WHERE `login_user`='".$login."';", 'num_row', '')!=1) {
      //~ не найден такой пользователь
      $error[]='Пользователь с таким именем не найден';
      return $this->error_print($error);
    } else {
      //~ проверка email
      if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) $error[]='Введен не корректный email';
      if ($mail != $db_inf['mail_user']) $error[]='Введенный email не соответствует введенному при регистрации ';
      if (!isset($error)) {
        //~ восстанавливаем пароль
        $new_passwd = $this->generateCode(8);
        $new_passwd_sql = md5($new_passwd.'lol');
        $message = "Вы запросили восстановление пароля на сайте %sitename% для учетной записи ".$db_inf['login_user']." \nВаш новый пароль: ".$new_passwd."\n\n С уважением администрация сайта %sitename%.";
        if (mail($mail, "Восстановление пароля", $message, "From: webmaster@sitename.ru\r\n"."Reply-To: webmaster@sitename.ru\r\n"."X-Mailer: PHP/" . phpversion())) {
          //~ почта отправлена, обновляем пароль в базе
          $db->query("UPDATE `users` SET `passwd_user`='".$new_passwd_sql."' WHERE `id_user` = ".$db_inf['id_user'].";", '', '');
          //~ все успешно - возвращаем положительный ответ
        return 'good';
        } else {
          //~ ошибка при отправке письма
          $error[]='В данный момент восстановление пароля не возможно, свяжитесь с администрацией сайта';
          return $this->error_print($error);
        }
      } else return $this->error_print($error);
    }
  }

Метод для восстановления пароля. Экранируем введенный логин и ищем такого пользователя в базе данных. Если не найден — сообщение об ошибке. Если найден то проверяем введенный email (он должен соответствовать указанному при регистрации). Генерируем новый пароль отправляем его на почту и заносим в базу.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  ###
  # Функция генерации случайной строки
  function generateCode($length) {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRQSTUVWXYZ0123456789";
    $code = "";
    $clen = strlen($chars) - 1;  
    while (strlen($code) < $length) {
      $code .= $chars[mt_rand(0,$clen)];  
    }
    return $code;
  }


  ###
  # Формирование списка ошибок
  function error_print($error) {
    $r='<h2>Произошли следующие ошибки:</h2>'."\n".'<ul>';
    foreach($error as $key=>$value) {
      $r.='<li>'.$value.'</li>';
    }
    return $r.'</ul>';
  }

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

Вот впрочем и все. Собственно как я уже говорил здесь еще много чего можно улучшить, добавить. Некоторые вещи лучше сделать вообще по другому. Но для примера более сложной авторизации чем предыдущие — более чем сойдет.

Код можно взять из моего репозитория на github.

Теги: , , , ,

57 комментариев к “Авторизация на сессиях и куках, PHP+MySQL v.2”

  1. Владимр
    Октябрь 11th, 2013 at 19:42
    1

    Почему после авторизации вылетает
    «Warning: Cannot modify header information — headers already sent by (output started at Z:\home\localhost\www\greencoal\index.php:11) in Z:\home\localhost\www\greencoal\module.php on line 121

    Warning: Cannot modify header information — headers already sent by (output started at Z:\home\localhost\www\greencoal\index.php:11) in Z:\home\localhost\www\greencoal\module.php on line 122
    Добро пожаловать admin»

    [Ответить]

    ZekMan Reply:

    Видимо вы что то изменили в коде. Проверьте. Сессия должна запускаться до вывода какого либо текста. Т.е. в самом начале файла.
    В моем коде она запускается в conf.php, который подключается в самом начале.
    Если же у вас нету никакого вывода до старта сессии — проверьте, файл должен быть сохранен в UTF-8 без BOM

    [Ответить]

  2. Дмитрий
    Октябрь 16th, 2013 at 18:39
    2

    ошибка в module.php — надо
    mysql_connect($db_host, $db_login, $db_passwd) or die («MySQL Error: » . mysql_error()); //~ устанавливаем подключение с бд
    вместо Login было name

    [Ответить]

    ZekMan Reply:

    Спасибо, поправил

    [Ответить]

  3. Дмитрий
    Октябрь 28th, 2013 at 19:41
    3

    $auth = new auth(); //~ Создаем новый объект класса

    Ругается на эту строчку, говорит:

    Fatal error: Class ‘auth’ not found in
    Z:\home\localhost\www\test\index.php on line 14

    [Ответить]

    ZekMan Reply:

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

    [Ответить]

  4. WisesT
    Ноябрь 4th, 2013 at 17:24
    4

    Приветствую!
    Создал файлик conf.php
    Вписал логин и пароль + базу. Сохранил в UTF-8 без ВОМ

    далее создал файлик index1.php (index.php уже есть).
    Скопировал туда весь Ваш код, но после сохранения (опять таки в UTF-8 без ВОМ) страница не отображается. Просто белый экран. Прошу Вашей подсказки.

    [Ответить]

    ZekMan Reply:

    Для начала попробуйте скачать исходники а не самим создавать файлы, ссылку я уже давал в конце статьи, вот ссылка сразу на архив.

    Если ситуация повторяется или же Вы хотите разобраться в вашей проблеме, то для начала проверьте включен ли вывод ошибок в php.
    Вы можете включить вывод ошибок в php.ini, или функциями error_reporting и ini_set, или же в .htaccess.

    Белый экран говорит о том что происходит какая то ошибка которая не обрабатывается и у вас выключен вывод ошибок.

    [Ответить]

  5. WisesT
    Ноябрь 6th, 2013 at 12:31
    5

    Хмм. Странно. Скачал архив по ссылке и все отлично сработало… Удивлен.
    В любом случае, спасибо за статью и пример.

    [Ответить]

  6. WisesT
    Ноябрь 6th, 2013 at 14:14
    6

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

    1
    2
    //~ Проверка авторизации
    if ($auth-&gt;check()) $r.='Добро пожаловать '.$_SESSION['login_user'].'<a href="?exit" rel="nofollow">Выйти</a>';

    но не пойму как.
    Пробовал организовать переменную $r вот таким образом

    1
    2
    $r=require("contacts.php");
    echo $r;

    но это же не дело.

    Подскажите, как можно Вашим примером закрыть несколько страниц под авторизацию?

    [Ответить]

    ZekMan Reply:

    Тут все очень просто, достаточно сделать:

    1
    2
    3
    4
    if (!$auth->check()) {
    //~ совершаем процедуру выхода
    $auth->exit_user();
    }

    Т.е. на всех страницах которая должна показываться только авторизованным пользователям — мы совершаем проверку авторизации в начале файла, и если пользователь не авторизован — совершаем выход-перенаправление на страницу входа.

    [Ответить]

  7. alesel
    Ноябрь 12th, 2013 at 21:58
    7

    Fatal error: Call to a member function check() on a non-object in
    после того, как поставил проверку на авторизацию

    [Ответить]

    ZekMan Reply:

    Вы подключили файл с классом авторизации?

    [Ответить]

  8. alesel
    Ноябрь 14th, 2013 at 19:30
    8

    Да. Вот пример:
    include_once(‘conf.php’);
    //var_dump($auth->check);
    if (!$auth->check()) {
    //~ совершаем процедуру выхода
    $auth->exit_user();

    }

    [Ответить]

    ZekMan Reply:

    Вы забыли $auth=new auth;
    http://www.php.net/manual/ru/language.oop5.basic.php

    [Ответить]

  9. alesel
    Ноябрь 14th, 2013 at 19:32
    9

    Я так понимаю отдельно модуль.пхп подключать не нужно, ведь он в конфе уже прописан.

    [Ответить]

    ZekMan Reply:

    Да, правильно.

    [Ответить]

  10. alesel
    Ноябрь 17th, 2013 at 13:10
    10

    Спасибо 🙂 Будем изучать

    [Ответить]

  11. shkid
    Ноябрь 21st, 2013 at 18:57
    11

    Спасибо за статью! Можете подробнее описать как защитить несколько файлов?

    [Ответить]

    ZekMan Reply:

    Посмотрите чуть выше в комментариях — я давал пример

    [Ответить]

  12. jet22
    Декабрь 23rd, 2013 at 17:11
    12

    Как замечательно что существуют такие люди как Вы 🙂 Спасибо за статью, код и объяснения. Все очень доступно!

    [Ответить]

    ZekMan Reply:

    Всегда рад помочь!)

    [Ответить]

  13. KpecT
    Январь 11th, 2014 at 04:19
    13

    не нашел обещаную статью по авторизации через вконтакт (и другие соц сети по возможности) очень буду рад почитать у вас.. А если вы мне скините на почту код для авторизацией вк (с самыми малыми возможностями) мне всего лишь то нужно чтобы пользователь зашел на сайт через вк, будучи авторизованным ввел необходимый цифры в 2 тектовых поля (но всего один раз, т.е пользователь не мог вводить данные повторно с одного и тогоже аккаунта вк) проблема в том что в php я совсем плох.. надо садиться и заниматься, но пока нет времени из-за учебы и работы, а сделать простенькую авторизацию нужно как можно быстрей.. код для записи в мою бд инфы с тектовых полей уже есть.. но пока проверка только по ip (не дает с одного ip дважды отправить данные) но так как больштнство юзеров с динамикой, хотим привязать данные к вк (понимаю что это мало чем поможет, но для нас будет уже не плохим вариантом, не смотря на то что создать акк вк не сложно, аудитория весьма узкого профиля, и восновном состоит из школьников.) буду очень благодарен если отпишите мне на почту

    [Ответить]

    ZekMan Reply:

    К сожалению пока что до нее не дошли руки. Может попробуете loginza?

    [Ответить]

  14. Кирилл
    Январь 20th, 2014 at 01:08
    14

    Доброго времени суток, спасибо большое за статью, помогла разобраться в нескольких вопросах!

    Только, если я не ошибаюсь, у Вас ошибка в описании файла «module.php».
    Вы начинаете с описания класса «mysql», а далее пишите «Далее опишу методы класса mysql», должно быть «Далее опишу методы класса auth», если я правильно понимаю:) отсюда видимо и ошибка у Дмитрия в 3-ем посте.

    И я бы тоже хотел проконсультироваться:

    Ошибка: «Cannot modify header information — headers already sent by…» при авторизации

    Она появляется если код написан в body после нескольких div, если же этот код поместить в самое начало файла до doctype, где подключается conf.php, то все работает. Файл сохранен в utf8 без bom, пробелов и скобок на ближайшем расстоянии не имеется, пробовал несколько раз уже переписывать код, все равно такая проблема.

    Ругается на строчку в которой открывается <?php

    [Ответить]

    ZekMan Reply:

    Просмотрите еще раз ваш вывод. Headers (Старт сессии, отправка заголовков и т.д.) должна производиться до какого либо вывода на экран. Т.е. Начало файла

    [Ответить]

  15. char
    Январь 21st, 2014 at 01:35
    15

    спасибо за статью все четко расписано и ясно !

    [Ответить]

  16. auth
    Январь 27th, 2014 at 10:00
    16

    У меня такая же ошибка:
    Fatal error: Class ‘auth’ not found in C:\OpenServer\domains\site\index.php on line 14
    Можете подробнее написать решение этой проблемы?

    [Ответить]

    ZekMan Reply:

    Не найден класс auth — возможно вы его не подключили. Сверьте ваш код с исходниками которые прилагаются в конце статьи.

    [Ответить]

  17. ser-gamov@yandex.ru
    Январь 30th, 2014 at 13:58
    17

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

    [Ответить]

  18. Михаил
    Январь 31st, 2014 at 14:58
    18

    Добрый день. Поставил на свой denwer ваш архив с авторизацией и регистрацией. Все работает. Регистрация и авторизация проходит. Однако. Как только я закрываю браузер и вхожу снова ту да же. Снова просит авторизоваться. Зачем? если мы используем в вашем коде куки?
    P.S. Куки записаны в табличку в mysql при регистрации.

    [Ответить]

    ZekMan Reply:

    Проверьте выставляются ли куки. Вообще Denwer не самая лучшая сборка сервера — поглядите в сторону XAMPP

    [Ответить]

  19. Алексей
    Февраль 1st, 2014 at 11:06
    19

    Здравствуйте. Все работает, но все равно вылазит
    Warning: Cannot modify header information — headers already sent by

    Жалуется на эти строчки при входе
    setcookie(«id_user», $_SESSION[‘id_user’], time()+3600*24*14);
    setcookie(«code_user», $r_code, time()+3600*24*14);
    и на эти при выходе
    setcookie(«id_user», », time()-3600);
    setcookie(«code_user», », time()-3600);
    header(«Location: index.php»);

    [Ответить]

  20. Алексей
    Февраль 1st, 2014 at 11:56
    20

    На хостинге все работает без ошибок))) А на локальном нет. Видимо надо какие-то настройки менять на локальном в php.ini

    [Ответить]

    ZekMan Reply:

    Смотрите отличия в конфигах, может что всплывет

    [Ответить]

  21. Михаил
    Февраль 4th, 2014 at 15:36
    21

    Заметил, что куки записываются только для 5-ти браузеров. Причем, если одновременно зайти на сайт через 5-ть браузеров, а затем выйти из одного — то если снова открыть страницу где авторизовывались — куки не сохраняются. Приходится заного авторизовываться. Проверял на хостинге.

    [Ответить]

    ZekMan Reply:

    Конкретизируйте пожалуйста. Для каких браузеров не были записаны куки? Были ли включены куки в этих браузерах?

    Одновременно вы можете зайти только под одним браузером. Мы так же сверяем user_agent браузера для точной идентификации кука/пользователь/браузер.

    [Ответить]

  22. Чочо Ололоев
    Февраль 12th, 2014 at 14:00
    22

    Спасибо за отличную статью! А почему бы не использовать mysqli и не переписать всё в стиле ООП?

    [Ответить]

    ZekMan Reply:

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

    Вообще имхо что 90% статей и манов по PHP (да и других языков) ориентированы в первую очередь на нулевых программистов. Для всех остальных есть офф документация и мозги))))

    Но может вы правы и стоит написать расширенную версию.

    [Ответить]

  23. Михаил
    Февраль 12th, 2014 at 15:43
    23

    Вон оно че)
    Куки включены во всех 6-ти браузерах. Все браузеры обновлены до последней версии. Вот эти браузеры: Chrome, Opera, Firefox, Safari, Yandex, IE. Я имел ввиду одновременно входим на всех 6-ти браузерах на сайт — то есть сначала через один браузер и не выходя из авторизации через другой и так далее до 6-того. ТО есть получается, что мы вошли через 6-ть браузеров. Однако, если посмотреть что пишется в базу — то там пишутся сессии только для 5-ти браузеров. Можете сами это проверить у себя.
    Кроме того, если же теперь обновить страницу на всех 6-ти браузеров не выходя из авторизации, то окажется, что в 2-3-ех из них — авторизация может остаться, а в других она слетает.
    Зачем я это проверял? Не знаю. Просто хотелось чтобы можно одновременно через все браузеры входить на сайт. Какая разница? Пусть человек заходит через несколько браузеров одновременно и сидит на сайте, пусть даже с одного IP. По мне так лучше пусть так будет, чем делать какое-то ограничение???

    [Ответить]

    ZekMan Reply:

    Ограничение не делается. User Agent мы сверяем только если отсутствует сессия (как в прочем и куки мы сверяем только в случае отсутствия сессий).
    Т.е. если нету в коде логических ошибок то делается вот что:
    Первая авторизация — стартует сессия, ставятся куки, обновляется запись в бд.
    Заходя в течении сессии — мы не проверяем куки, нам достаточно жизни сессии, но куки мы обновляем.
    Заходя со второго/n браузера — стартует сессия, ставятся куки, обновляется запись в бд.
    При этом если мы обновляем страницу в предыдущем браузере то тут есть два варианта развития событий:
    1 — есть сессия, обновляется время жизни сессии (автоматически веб сервером), обновляются куки, обновляется запись в бд.
    2 — нету сессии, сверяются куки, естественно они не верны т.к. обновлены другим браузером. Требуется повторная авторизация.

    [Ответить]

  24. Михаил
    Февраль 12th, 2014 at 21:34
    24

    Я просто хочу уточнить, ваш скрипт рабочий. В этом нет сомнений.
    Является ли Ваш скрипт одним из самых правильных, которые применяют в большинстве своем популярные сайты (например, вконтакте)?

    [Ответить]

    ZekMan Reply:

    В каждом крупном проекте свои велосипеды, не стоит сравнивать работу одного программиста и целого штата который постоянно работает на совершенствованием одного своего проекта.

    [Ответить]

  25. Михаил
    Февраль 12th, 2014 at 21:53
    25

    Сейчас то же самое проверил на сайте vk.com. Зашел одновременно с 6-ти браузеров. После обновления страниц ни на одном из браузеров не слетает аутентификация. То есть код написан правильно.
    Вопрос: Могли бы Вы доработать свой код, чтобы варианта номер 2 не было? Например, такой алгоритм:
    1) Я авторизовался с 1-ого браузера. Ставятся куки. Заносятся в базу.
    2) Я авторизовался со 2-оо браузера. Снова ставятся куки, но они уже не перезаписывают предыдущие, а, например, просто добавляются к этому же аккаунту. Заносятся в базу.
    3) Я авторизовался с 3-его браузера. Снова ставятся куки. Добавляются к предыдущим. Заносятся в базу.
    И так далее.
    Мне кажется, это было бы замечательно, если бы Вы смогли доработать свой код до такого совершенства!
    Спасибо!

    [Ответить]

    ZekMan Reply:

    Доделывать и дорабатывать я ее буду однозначно, в этом нет сомнений. Сейчас в ней и правда присутствуют некоторые сомнительные решения и места которые бы хотелось переработать. Например общий секретный ключ, его использование не совсем верное, или если быть точнее имеет свои недостатки.

    [Ответить]

  26. Михаил
    Февраль 13th, 2014 at 14:45
    26

    Сколько Вам понадобится времени для устранения этих недостатков? Можете ли вы применить предложенный мной алгоритм к вашему коду как можно раньше?

    [Ответить]

    ZMan Reply:

    Вопрос в виде сделайте мне быстро я вам заплачу или сделайте мне быстро просто потому что мне это срочно нужно?
    Если первое то напишите мне на почту. Если второе — то как только руки дойдут.

    [Ответить]

  27. Алексей
    Февраль 21st, 2014 at 15:59
    27

    Что -то у меня не выходит запаролить вторую страницу, все равно она открывается.
    Можете выложить пример или подсказать
    Этот код не срабатывает, и не переадресовывает на главную
    if (!$auth->check()) {
    //~ совершаем процедуру выхода
    $auth->exit_user();
    }

    [Ответить]

  28. Алексей
    Февраль 21st, 2014 at 17:32
    28

    Все спасибо не нужно, перекодировал все файлы в utf без boom и убрал все пробелы перед функцией header…Кароче все работает. Функцию header нужно как и session start() вызывать в самом начале файла.

    [Ответить]

  29. Алексей
    Февраль 23rd, 2014 at 10:20
    29

    Еще хотел уточнить…Время сессии у нас по умолчанию 30мин, как я понял, а куки хранятся две недели. То есть пока я не нажму кнопку «выход», у меня две недели будет доступ к странице. Так или не так?

    [Ответить]

Страницы комментариев

Написать комментарий