Вход пользователей
Аутентификация — проверка, что посетитель тот, за кого себя выдаёт: он вводит почту и пароль, сайт сверяет их с базой и запоминает результат в сессии. В Laminas за это отвечает пакет laminas-authentication. Он не знает, где хранятся пользователи: проверку выполняет адаптер — класс, который вы пишете сами.
Установка и таблица
composer require laminas/laminas-authenticationПакет хранит вошедшего пользователя в сессии, поэтому нужен laminas-session из урока о сессиях. Создайте таблицу:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL
);Хеш пароля для первой записи получите командой php -r "echo password_hash('secret', PASSWORD_DEFAULT);" и вставьте строку запросом INSERT.
Для чтения таблицы создайте классы Blog\Model\User и Blog\Model\UserTable по образцу Post и PostTable. Таблице нужен один метод — findByEmail(string $email): ?User, внутри которого select(['email' => $email])->current().
Адаптер
// module/Blog/src/Auth/PasswordAdapter.php
namespace Blog\Auth;
use Blog\Model\UserTable;
use Laminas\Authentication\Adapter\AdapterInterface;
use Laminas\Authentication\Result;
class PasswordAdapter implements AdapterInterface
{
private string $email = '';
private string $password = '';
public function __construct(private UserTable $users) {}
public function setCredentials(string $email, string $password): void
{
$this->email = $email;
$this->password = $password;
}
public function authenticate(): Result
{
$user = $this->users->findByEmail($this->email);
if ($user === null || !password_verify($this->password, $user->passwordHash)) {
return new Result(Result::FAILURE_CREDENTIAL_INVALID, null, ['Неверная почта или пароль.']);
}
return new Result(Result::SUCCESS, ['id' => $user->id, 'email' => $user->email]);
}
}Метод authenticate() возвращает объект Result. Его второй аргумент — «личность» (identity): данные, которые останутся в сессии.
Регистрация сервисов
// module/Blog/config/module.config.php
// в начале файла: use Laminas\Authentication\AuthenticationService;
'service_manager' => [
'factories' => [
AuthenticationService::class => InvokableFactory::class,
Auth\PasswordAdapter::class => ReflectionBasedAbstractFactory::class,
],
],AuthenticationService — готовый класс пакета: он вызывает адаптер и хранит личность в сессии.
Вход и выход
Создайте контроллер и зарегистрируйте его в controllers через ReflectionBasedAbstractFactory:
// module/Blog/src/Controller/AuthController.php
namespace Blog\Controller;
use Blog\Auth\PasswordAdapter;
use Laminas\Authentication\AuthenticationService;
use Laminas\Mvc\Controller\AbstractActionController;
use Laminas\View\Model\ViewModel;
class AuthController extends AbstractActionController
{
public function __construct(
private AuthenticationService $auth,
private PasswordAdapter $adapter,
) {}
public function loginAction()
{
$request = $this->getRequest();
if (!$request->isPost()) {
return new ViewModel(['error' => null]);
}
$this->adapter->setCredentials(
(string) $request->getPost('email'),
(string) $request->getPost('password'),
);
$result = $this->auth->authenticate($this->adapter);
if ($result->isValid()) {
return $this->redirect()->toRoute('blog');
}
return new ViewModel(['error' => $result->getMessages()[0]]);
}
public function logoutAction()
{
$this->auth->clearIdentity();
return $this->redirect()->toRoute('login');
}
}Добавьте два маршрута типа Literal: login с адресом /login и logout с адресом /logout. Шаблон module/Blog/view/blog/auth/login.phtml содержит форму с полями email и password и выводит $this->error. Откройте /login, введите данные пользователя — после входа откроется /blog.
Текущий пользователь
| Вызов | Что возвращает |
|---|---|
$this->auth->hasIdentity() | true, если посетитель вошёл |
$this->auth->getIdentity() | Массив из Result: id и email |
$this->auth->clearIdentity() | Забывает пользователя |
В шаблонах и макете ту же личность возвращает помощник $this->identity(): он вернёт null для гостя.
Частая ошибка
Пароль хранят только в виде хеша password_hash() и сравнивают через password_verify(). Сравнение открытых паролей или хешей md5() делает утечку базы катастрофой. Сообщение об ошибке входа должно быть одно — «Неверная почта или пароль» — без уточнения, что именно не подошло.