#10 php Простейшая авторизация: форма входа, пароль и $_SESSION

Пора собрать всё вместе: формы, сессии и редиректы дадут нам полноценную (пусть и крошечную) авторизацию. Сделаем страницу входа с логином и паролем — и «секретную» страницу, которую видно только вошедшим. Так работают входы на всех сайтах, различие лишь в том, где хранится список пользователей.

Немного теории

Основа — сессии: после успешной проверки мы запишем в $_SESSION факт «этот посетитель вошёл как admin», и все страницы сайта смогут это проверить. Поле для пароля описано в учебнике: поле для ввода пароля — оно такое же текстовое, но символы не показываются. Альтернативный, «древний» способ узнавать пользователя — HTTP-аутентификация — упомянем для кругозора: сегодня для входа на сайте почти всегда используют именно сессии.

Всё остальное нам уже знакомо: самопроверка формы из урока про отправку формы и редиректы из урока про обработчик.

Файл первый: страница входа

Логин и пароль для простоты зашиты в код: admin / 12345.

<?php
session_start();

// уже вошли? - сразу на закрытую страницу
if (isset($_SESSION['user'])) {
    header('Location: secret.php');
    exit;
}

$login = '';
$password = '';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['login'])) {
        $login = $_POST['login'];
    }
    if (isset($_POST['password'])) {
        $password = $_POST['password'];
    }
    if ($login === 'admin' && $password === '12345') {
        $_SESSION['user'] = $login;
        header('Location: secret.php');
        exit;
    }
    $error = 'Неверный логин или пароль.';
}
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Вход</title>
</head>
<body>
  <h1>Вход</h1>
<?php if ($error !== '') { ?>
  <p><?php echo htmlspecialchars($error); ?></p>
<?php } ?>
  <form action="" method="post">
    <p>Логин:
      <input type="text" name="login"
             value="<?php echo htmlspecialchars($login); ?>">
    </p>
    <p>Пароль:
      <input type="password" name="password">
    </p>
    <p><button type="submit">Войти</button></p>
  </form>
</body>
</html>

Что здесь происходит:

  • если посетитель уже вошёл (в сессии есть user) — форма входа ему не нужна, отправляем его сразу на закрытую страницу;
  • форма обрабатывает сама себя: это знакомый приём с проверкой REQUEST_METHOD;
  • при совпадении логина и пароля пишем в сессию $_SESSION['user'] — с этого момента все страницы сайта «знают», что посетитель вошёл;
  • после успеха — редирект (тот же приём, что в уроке про форму и обработчик), а при неудаче — сообщение и форма с сохранённым логином.

Файл второй: закрытая страница

secret.php — «только для своих»:

<?php
session_start();

// не вошёл? - на страницу входа
if (!isset($_SESSION['user'])) {
    header('Location: login.php');
    exit;
}
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Секретная страница</title>
</head>
<body>
  <h1>Только для своих</h1>
  <p>Привет, <?php echo htmlspecialchars($_SESSION['user']); ?>!</p>
  <p>Здесь мог бы быть ваш личный кабинет.</p>
</body>
</html>

Вся «охрана» — три строки в начале: нет в сессии user — значит, не входил, и страница сама отправляет на форму входа. Этот блок-«страж» ставят в начало каждой закрытой страницы сайта.

Прогоняем сценарий

  1. откройте http://localhost:19791/lessons/lesson10-auth/secret.php не входя — вас перебросит на login.php;
  2. введите неверный пароль — появится «Неверный логин или пароль», логин останется в поле;
  3. введите admin / 12345 — попадёте на закрытую страницу: «Привет, admin!»;
  4. откройте secret.php ещё раз — пускает без формы: сервер узнаёт вас по куке PHPSESSID (урок про сессии);
  5. а теперь откройте закрытую страницу в режиме инкогнито — снова на вход: у того браузера своя, пустая сессия.

Честное замечание про пароли

Пара «admin/12345», зашитая в код, — приём только для учёбы. В реальных системах пользователи лежат в базе, пароли хранят в виде хэшей (для этого в PHP есть функции password_hash() и password_verify()), а сам вход обслуживается по HTTPS. И заметьте нашу фразу ошибки: «Неверный логин или пароль» — без уточнения, что именно неверно: так злоумышленнику сложнее понять, существует ли логин.

Попробуйте сами

  • поменяйте логин и пароль в коде и проверьте оба сценария ещё раз;
  • сделайте «вторую секретную страницу» (например, secret2.php) с тем же стражем — вход один, закрытых страниц сколько угодно;
  • посложнее: добавьте на страницу входа сообщение «Вы уже вошли» для тех, кто попал туда с живой сессией (подсказка: перед редиректом можно добавить GET-параметр — так мы передавали флаг sent в уроке про форму и обработчик).

Итоги

  • факт входа хранят в сессии: $_SESSION['user'];
  • каждая закрытая страница начинается со «стража»: нет метки — редирект на вход;
  • форма входа обрабатывает себя сама и после успеха делает редирект;
  • реальные пароли хранят хэшами, а не открытым текстом.

В следующем уроке доделаем картину: сделаем «выход» — очистим сессию в отдельном logout.php.