Аутентификация
Представим интернет-магазин. Все его страницы можно разделить на две половины: публичные и приватные.
К публичным относятся страницы каталога, информации о товаре, условия доставки и так далее. К приватным — корзина покупок, история заказов. Совершенно очевидно, что корзина покупок у каждого покупателя должна быть своя, а иметь к ней доступ должен только сам владелец и никто больше.
Проверка доступа пользователей к сайту и называется аутентификацией. Весь процесс авторизации всегда состоит из нескольких шагов:
- При попытке доступа к закрытой части сайта, пользователь видит форму ввода логина и пароля.
- Форма отправляется, а полученные данные сравниваются с действительным логином и паролем существующего пользователя. Эта сверка и называется аутентификацей.
- Если данные совпадают, то пользователь считается аутентифицированным и процесс авторизации продолжается. Пользователю выдаётся роль и связанные с ней права, в том числе получает доступ к приватной части сайта.
- При повторном открытии этой страницы пользователь не должен повторно вводить пароль, если он уже делал это в рамках текущего сеанса.
Отличие аутентификации и авторизации
Следует различать два термина: аутентификация и авторизация.
Аутентификация — проверка подлинности предоставленного пользователем удостоверяющей информации: пары логин-пароль. И проверка наличия учётной записи с такими данными Авторизация — Это связывание сессии пользователя с определённой учётной записью и выдача прав соответствующих роли данной учётной записи.
Аутентификация – это лишь часть процесса авторизации. Аутентификация – это проверка данных. А авторизация – выдача прав определённой роли.
Таблица пользователей
CREATE TABLE `users` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci;
Начальный загрузчик
Роль начального загрузчика у нас выполняет файл setting.php или core.php. Который стартует сессию и создаёт подключение к БД и записывает это подключение в переменную $pdo.
Форма регистрации
Этот html размещаем в файле register.php
<?require_once __DIR__.'/setting.php';?>
require_once($setting['TEMPLATE_PATH'] . 'head.php');?>
<body>
<?require_once($setting['TEMPLATE_PATH'] . 'header.php');?>
<div class="error" <?if (empty($error['register'])) {?>style="display: none;"<?}?>>
<?foreach($error['register'] as $message) {?>
<p><?=$message;?></p>
<?}?>
</div>
<?if ($success['register']) {?>
<p><?=$success['register'];?></p>
<?} else {?>
<form action="/form/do_reg.php" method="POST">
<input type="text" name="login" placeholder="Login" />
<input type="text" name="name" placeholder="Name"/>
<input type="email" name="email" placeholder="Email Address"/>
<input type="password" name="password" placeholder="Password"/>
<button type="submit" class="btn btn-default">Регистрация</button>
</form>
<?}?>
Обработка регистрации
Напишем обработчик, файл do_reg.php
<?php require_once('../setting.php');
$error = ['register' => []];
$success = ['register' => false];
if (isset($_POST['login']) && !$_POST['login']) {
$error['register'][] = 'Пустой логин';
}
// Проверим, не занято ли имя пользователя
$stmt = $pdo->prepare("SELECT * FROM `wshop_user` WHERE `login` = :login");
$stmt->execute(['login' => $_POST['login']]);
if ($stmt->rowCount() > 0) {
$error['register'][] = 'Это имя пользователя уже занято.';
}
if (empty($error['register'])) {
// Добавим пользователя в базу
$stmt = $pdo->prepare("INSERT INTO `wshop_user` (`login`, `password`, `name`, `role_id`) VALUES (:login, :password, :name, 3)");
$stmt->execute([
'login' => $_POST['login'],
'password' => password_hash($_POST['password'], PASSWORD_DEFAULT),
'name' => $_POST['name'],
]);
$success['register'] = 'Пользователь зарегистрирован';
}
require('../page/login.php');
В самом начале подключим наш “загрузчик”.
Потом проверим, не занято ли имя пользователя. Для этого сделаем выборку из таблицы указав в условии полученное из формы имя пользователя. Обратите внимание, для запросов здесь и далее мы будем использовать подготовленные запросы, что обезопасит нас от SQL-инъекций. Для этого в тексте SQL-запроса мы указываем специальные плейсхолдеры, а при выполнении ассоциируем с ними ненадёжные данные (ненадёжными данными следует считать всё, что приходит из вне – $_GET, $_POST, $_REQUEST, $_COOKIE). После выполнения запроса мы просто проверим количество возвращённых строк. Если их больше нуля, то имя пользователя уже занято. В этом случае мы выведем сообщение и вернём пользователя на форму регистрации.
Написано “больше нуля”, но по факту, из-за того, что поле name
в таблице уникальное, rowCount()
может нам вернуть лишь два возможных значения: 0
и 1
.
Форма авторизации
Этот html размещаем в файле login.php
<?require_once __DIR__.'/setting.php';?>
require_once($setting['TEMPLATE_PATH'] . 'head.php');?>
<body>
<?require_once($setting['TEMPLATE_PATH'] . 'header.php');?>
<div class="error" <?if (empty($error['login'])) {?>style="display: none;"<?}?>>
<?foreach($error['login'] as $message) {?>
<p><?=$message?></p>
<?}?>
</div>
<form action="/form/do_login.php" method="POST">
<input type="text" name="login" placeholder="Login" />
<input type="password" name="password" placeholder="Password"/>
<span>
<input type="checkbox" class="checkbox">
Запомнить меня
</span>
<button type="submit" class="btn btn-default">Войти</button>
</form>
Обработка авторизации
<?require_once('../setting.php');
$error = ['login' => []];
if (isset($_POST['login']) && !$_POST['login']) {
$error['login'][] = 'Пустой логин';
} elseif (isset($_POST['login'])) {
// проверяем наличие пользователя с указанным юзернеймом
$stmt = $pdo->prepare("SELECT * FROM `wshop_user` WHERE `login` = :login");
$stmt->execute(['login' => $_POST['login']]);
if (!$stmt->rowCount()) {
$error['login'][] = 'Пользователь с такими данными не зарегистрирован';
} else {
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// проверяем пароль
// $error['login'][] = $_POST['password'] . ' - ' . $user['password'];
if (password_verify($_POST['password'], $user['password'])) {
unset($user['password']);
$_SESSION['user'] = $user;
// лучше перекинуть в ЛК /page/account/
header('Location: /');
die;
} else {
$error['login'][] = 'Пароль неверен';
}
}
}
require('../page/login.php');
Использование сессии для контроля доступа
Сессии чаще всего используются для хранения информации о залогиненном пользователе.
Принцип работы простой: внутри сценария, ответственного за обработку формы входа, открывается новая сессия, куда записывается информация о вошедшем пользователе. Такой информацией может быть ассоциативный массив со всеми значениями из соответствующей записи из базы данных.
Затем добавим код, проверяющий существование сессии в сценарии, который должен быть закрыт от анонимных пользователей.
Если сессия пуста, значит, пользователь не выполнял вход на сайт и доступа к данной странице не имеет. В этом случае можно вернуть код ответа 403 и показать сообщение об ошибке.
Задача
1 создайте формы авторизации и регистрации убедитесь, что они работают.
2 Напишите форму и обработчик оформления заказа.