Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Вход пользователей

Аутентификация — проверка, что посетитель тот, за кого себя выдаёт: он вводит почту и пароль, сайт сверяет их с базой и запоминает результат в сессии. В 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() делает утечку базы катастрофой. Сообщение об ошибке входа должно быть одно — «Неверная почта или пароль» — без уточнения, что именно не подошло.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.