sinbadxiii / phalcon-auth
Phalcon Auth - Guard-Based Authentication
Installs: 7 771
Dependents: 2
Suggesters: 0
Security: 0
Stars: 8
Watchers: 2
Forks: 6
Open Issues: 0
Requires
- php: ^7.4 || ^8.0
Requires (Dev)
- mockery/mockery: ^1.4
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-10-31 11:48:15 UTC
README
You can see an example of an application with authentication here sinbadxiii/phalcon-auth-example
Extended guards
Phalcon version
Unfortunately version 2 of the library no longer supports Phalcon 4.
PHP are supported
^7.4-8.1.
Install
Require the project using composer:
composer require "sinbadxiii/phalcon-auth:^v2.0.0"
Introduction
Phalcon Auth позволит вам создать систему аутентификации в вашем веб-приложении.
Система аутентификации имеет такие понятия как «Охранники» (Guard) и «Поставщики» (Provider), охранники определяют, как пользователи будут аутентифицироваться, например, используя стандартные Хранилища Сессии и файлов куки.
Провайдеры определяют, какие данные будут браться в качестве пользователей, и так же откуда будут извлекаться эти пользователи. Откуда будут извлекаться данные пользователей определяют Адаптеры (Adapter). Обычно это Phalcon\Mvc\Model
и построитель запросов к базе данных.
Кроме того есть другие варианты адаптеров: файл или массив с данными. Можно создать свой адаптер, реализуя интерфейс адаптера. Об этом поговорим чуть позже.
Guards и Providers не следует путать с «roles» и «permissions» ACL. Auth и ACL следует использовать вместе, если требуется более точная надстройка доступа к узлам приложения. Например использовать роль
manager
со специфическими правами.
Быстрый старт
Полностью пример готового приложения с аутентификацией доступен по адресу sinbadxiii/phalcon-auth-example. Это типовой проект на Phalcon, который можно использовать как старт нового приложения, либо же просто ознакомиться с возможностями аутентификации на примере данного приложения.
Логика работы
Общий принцип работы аутентификации заключается в том, что пользователь вводит свое имя пользователя и пароль через форму входа. Если эти учетные данные верны, приложение сохранит информацию об аутентифицированном пользователе в сессии пользователя и будет считать пользователя "аутентифицированным". В случае использования "Запомнить меня" может быть создан файл cookie, который содержит идентификатор сессии, чтобы последующие запросы к приложению могли быть связаны с нужным пользователем. После получения идентификатора сессии из файла cookie приложение извлечет данные пользователя.
Возьмем другой случай, когда удаленному сервису необходимо пройти аутентификацию для доступа к API, обычно файлы cookie не используются для аутентификации, поскольку веб-браузер отсутствует. Вместо этого удаленная служба отправляет токен API при каждом запросе. Приложение может проверить входящий токен по таблице действительных токенов API и "аутентифицировать" запрос как выполненный пользователем, связанным с этим токеном API.
Подготовка базы данных
Для использования данных из бд, понадобится создать таблицу users
.
Если необходимо будет использовать функцию "Запомнить меня" - RememberMe
, которая позволяет хранить сеанс аутентификации пользователя длительное время, то так же понадобится таблица users_remember_tokens
, ну и соответственно ее модель в виде App\Models\RememberToken
.
Для быстрого создания таблиц вы можете импортировать файлы из папки db/users.sql
, db/users_remember_tokens.sql
, а так же db/create_auth_token_users.sql
, если будете использовать в качестве Guard - Token, которому необходимо поле auth_token
для корректной работы.
Managers
При создании аутентификации вы можете воспользоваться одним из двух менеджеров: Sinbadxiii\PhalconAuth\Manager
или Sinbadxiii\PhalconAuth\ManagerFactory
.
Manager
Если вы строго придерживаетесь философии фреймворка Phalcon и хотите вручную настроить все компоненты аутентификации, то вам понадобится класс Sinbadxiii\PhalconAuth\Manager
- с помощью данного менеджера можно настроить охранника, адаптер поставщиков и распределить доступы пользователям.
use Sinbadxiii\PhalconAuth\Manager; use App\Models\User; use Sinbadxiii\PhalconAuth\Adapter\Model; use Sinbadxiii\PhalconAuth\Guard\Session; $auth = new Manager(); $configAdapter = [ 'model' => User::class, ]; $adapter = new Model($this->getSecurity(), $configAdapter); $guard = new Session( $adapter, $this->getSession(), $this->getCookies(), $this->getRequest(), $this->getEventsManager() ); $auth->addGuard("web", $guard, true); return $auth;
В результате получился менеджер, который будет искать пользователей через модель User
в таблице базе данных users
.
Результат аутентификации будет храниться в сессии, и куках, если выбрать "Запомнить меня".
В качестве других аргументов нужно передать сервис провайдеры $this->security
, $this->session
, $this->cookies
, $this->request
, $this->eventsManager
, которые будут необходимы при дальнейшем использовании охранника и адаптера поставщиков.
- public addGuard(string $nameGuard, GuardInterface $guard, bool $isDefault = false) - добавить охранника
- public guard(?string $name = null) - получить конкретного охранника или по заданного по дефолту
- public setDefaultGuard(GuardInterface $guard) - задать охранника по дефолту
- public getDefaultGuard() - получить охранника по дефолту
- public access(string $accessName) - назначить контроллеру определенный доступ
- public getAccess(string $accessName) - назначить требуемый доступ
- public setAccess(AccessInterface $access) - получить требуемый доступ
- public setAccessList(array $accessList) - зарегистрировать список доступов
- public addAccessList(array $accessList) - добавить список доступов
- public except(...$actions) - исключенные экшны из проверки доступа
- public only(...$actions) - обязательные экшны для проверки доступа
- public __call() - magic __call
Guards
На данный момент существует два вида Охранников, которые покроют 90% типовых задач создания аутентификации веб-приложений.
Это Sinbadxiii\PhalconAuth\Guard\Session
и Sinbadxiii\PhalconAuth\Guard\Token
, указывая одного из этих охранников вы выбираете, что будете использовать в своем приложении, аутентификацию на основе сессий или токена.
Предположительно Сессии вы будете использовать в веб-приложениях после логина в личный кабинет,а Токен, например, в микро приложениях в качестве api сервисов. Но ничего вам не мешает применять или комбинировать охранников в нестандартных приложениях.
Session Guard
use Sinbadxiii\PhalconAuth\Manager; use App\Models\User; use Sinbadxiii\PhalconAuth\Adapter\Model; use Sinbadxiii\PhalconAuth\Guard\Session; $auth = new Manager(); $configAdapter = [ 'model' => User::class, ]; $adapter = new Model($this->getSecurity(), $configAdapter); $guard = new Session( $adapter, $this->getSession(), $this->getCookies(), $this->getRequest(), $this->getEventsManager() ); $auth->addGuard("web", $guard, true); return $auth;
- public function __construct(AdapterInterface $adapter, SessionManagerInterface $session, Cookies $cookies, Request $request, EventsManagerInterface $eventsManager)
- public function attempt(array $credentials = [], $remember = false) - попытка аутентификации
- public function user() - получить аутентифицированного пользователя
- public function validate(array $credentials = []) - валидация входных данных
- public function getName() - получение имени сессии
- public function getRememberName() - имя куки при запомнить меня
- public function login(AuthenticatableInterface $user, bool $remember = false) - логин экземпляра пользователя
- public function loginById($id, bool $remember = false) - логин по Id пользователя
- public function once(array $credentials = []) - логин без сохранения пользователя в сессию
- public function logout() - выход
- public function getLastUserAttempted() - получение последнего попытавшегося залогиниться пользователя
- public function viaRemember() - проверка что пользователь был вытащен из Запомнить меня
- public function getUser() - получить пользователя
- public function setRequest(Request $request)
- public function setSession(SessionManagerInterface $session)
- public function setCookies(Cookies $cookies)
- public function getAdapter() - получить адаптер поставщика
- public function setAdapter(AdapterInterface $adapter) - назначить адаптера поставшика
Basic
- public function basic(string $field = 'email', array $extraConditions = []) - аутентификация через Basic Auth
- public function onceBasic(string $field = 'email', array $extraConditions = []) - аутентификация через Basic Auth без сохранения в сессию
Token Guard
Чтобы воспользоваться Sinbadxiii\PhalconAuth\Guard\Token
, необходимо в качестве второго аргумента передать конфиг с названиями имя параметра запроса и поля в хранилище данных пользователей, например, поле таблицы users
в бд:
[ ... 'inputKey' => 'auth_token', //имя параметра с токеном 'storageKey' => 'auth_token', //имя поля в хранилище пользователей ... ]
use Sinbadxiii\PhalconAuth\Manager; use App\Models\User; use Sinbadxiii\PhalconAuth\Adapter\Model; use Sinbadxiii\PhalconAuth\Guard\Token; $auth = new Manager(); $configAdapter = [ 'model' => User::class, ]; $configGuard = [ 'inputKey' => 'auth_token', 'storageKey' => 'auth_token', ]; $adapter = new Model($this->getSecurity(), $configAdapter); $guard = new Token( $adapter, $configGuard $this->getRequest() ); $auth->addGuard("api", $guard, true); return $auth;
Соответствено GET запрос должен будет иметь вид:
//GET
https://yourapidomain/api/v2/users?auth_token=fGaYgdGPSfEgT41r3F4fg33
POST запрос:
//POST //params POST request [ "auth_token": "fGaYgdGPSfEgT41r3F4fg33" ] https://yourapidomain/api/v2/users
или заголовок Authorization
:
Authorization: Bearer fGaYgdGPSfEgT41r3F4fg33 https://yourapidomain/api/v2/users
Помните, что каждый ваш запрос к приложению, должен сопровождаться параметром
auth_token
с токеном доступа.
- public function __construct(AdapterInterface $adapter, array $config, Request $request)
- public function user() - аутентифицированный пользователь
- public function validate(array $credentials = []) - валидация
- public function getTokenForRequest() - поулчить токен из запросов (GET, POST, Headers)
- public function setRequest(Request $request)
- public function getRequest()
- public function getAdapter()
- public function setAdapter(AdapterInterface $adapter)
Создание своего Охранника
<?php declare(strict_types=1); namespace Sinbadxiii\PhalconAuth\Guard; use Sinbadxiii\PhalconAuth\AuthenticatableInterface; interface GuardInterface { public function check(): bool; public function user(); public function setUser(AuthenticatableInterface $user); public function id(); public function guest(): bool; public function validate(array $credentials = []): bool; }
Реализуя интерфейс Sinbadxiii\PhalconAuth\Guard\GuardInterface
вы можете создать своего Guard.
Access
С помощью Доступов (Access) вы можете задавать и проверять требуемый доступ к тем или иным областям приложения, например в контроллер профиля пользователя разрешен доступ только аутентифицированным пользователям.
<?php declare(strict_types=1); namespace App\Controllers; class ProfileController extends ControllerBase { public function onConstruct() { $this->auth->access("auth"); } public function indexAction() { } }
В то время как к контроллеру регистрации, например, нужен доступ только неаутентифицированным пользователям - гостям:
<?php declare(strict_types=1); namespace App\Controllers; class RegisterController extends ControllerBase { public function onConstruct() { $this->auth->access("guest"); } public function indexAction() { } }
Задается доступ в конструкторе контроллера onConstruct()
.
Из коробки есть два основных вида доступа - аутентифицированный и гостевой:
Sinbadxiii\PhalconAuth\Access\Auth
Sinbadxiii\PhalconAuth\Access\Guest
Если доступ удовлетворяет условию в методе allowIf
, то дается разрешение на дальнейшее использование контроллера, например в дефолтном auth
условием является:
class Auth extends AbstractAccess { /** * @return bool */ public function allowedIf(): bool { if ($this->auth->check()) { return true; } return false; } }
$this->auth->check()
- проверка на аутентификацию пользователя, т.е. чтобы получить доступ к $this->auth->access('auth')
нужно быть аутентифицированным, а вот условие у $this->auth->access('guest')
прямо противоположно:
<?php namespace Sinbadxiii\PhalconAuth\Access; /** * Class Guest * @package Sinbadxiii\PhalconAuth\Access */ class Guest extends AbstractAccess { /** * @return bool */ public function allowedIf(): bool { if ($this->auth->guest()) { return true; } return false; } }
В случае если метод allowedIf()
вернет true
, то пользователь сможет идти дальше, если же результат будет равен false
, то сработает метод неудачи redirectTo()
, и приложение перенаправит пользователя, т.к. у каждого приложение логика перенаправлений может быть разная, то вам следует создать свои классы Access auth
и guest
, наследовав от дефолтных классов и переопределить метод redirectTo()
:
<?php namespace App\Security\Access; use Sinbadxiii\PhalconAuth\Access\Auth as AuthAccess;; class Auth extends AuthAccess { public function redirectTo() { if (isset($this->response)) { return $this->response->redirect("/login")->send(); } } }
и
<?php namespace App\Security\Access; use Sinbadxiii\PhalconAuth\Access\Guest as GuestAccess; class Guest extends GuestAccess { public function redirectTo() { if (isset($this->response)) { return $this->response->redirect("/profile")->send(); } } }
Чтобы создать свой Access, можно имплементировать интерфейс Sinbadxiii\PhalconAuth\Access\AccessInterface
:
<?php namespace Sinbadxiii\PhalconAuth\Access; /** * Interface for Sinbadxiii\PhalconAuth\Access */ interface AccessInterface { public function setExceptActions(...$actions): void; public function setOnlyActions(...$actions): void; public function isAllowed(): bool; public function redirectTo(); public function allowedIf(): bool; }
либо просто наследовав абстрактный класс Sinbadxiii\PhalconAuth\Access\AbstractAccess
для более быстрого и гибкого использования кастомных доступов, например, давайте создадим доступ для пользователей, имеющих роль админа:
<?php namespace App\Security\Access; use Sinbadxiii\PhalconAuth\Access\AbstractAccess; class Admin extends AbstractAccess { /** * @return bool */ public function allowedIf(): bool { if ($user = $this->auth->user() and $user->getRole() === "admin") { return true; } return false; } /** * @return void * @throws Exception */ public function redirectTo() { if (isset($this->response)) { return $this->response->redirect("/admin-login")->send(); } } }
или пример проверки доступа для Http Basic Auth:
<?php namespace App\Security\Access; use Sinbadxiii\PhalconAuth\Access\AbstractAccess; use Sinbadxiii\PhalconAuth\Exception; class AuthWithBasic extends AbstractAccess { /** * @return bool */ public function allowedIf(): bool { if ($this->auth->basic("email")) { return true; } return false; } /** * @return void * @throws Exception */ public function redirectTo() { throw new Exception("Basic: Invalid credentials."); } }
Регистрация доступов
Доступы должны быть зарегистрированы в системе аутентификации, если этого не сделать, то при запросе доступа $this->auth->access("auth")
будет выдаваться ошибка, типа:
Access with 'auth' name is not included in the access list
.
Чтобы зарегистрировать доступы в системе, необходимо создать некоторое промежуточное программное обеспечение, подтипа middleware и прикрепить его к dispatcher
приложения.
Минимальный вид класса App\Security\Authenticate
будет таков:
<?php declare(strict_types=1); namespace App\Security; use App\Security\Access\Auth; use App\Security\Access\Guest; use Sinbadxiii\PhalconAuth\Access\Authenticate as AuthMiddleware; /** * Class Authenticate * @package App\Security */ class Authenticate extends AuthMiddleware { protected array $accessList = [ 'auth' => Auth::class, 'guest' => Guest::class ]; }
А затем необходимо прикрепить его к сервиc-провайдеру dispatcher
:
$di->setShared('dispatcher', function () use ($di) { $dispatcher = new Phalcon\Mvc\Dispatcher(); $eventsManager = $di->getShared('eventsManager'); $eventsManager->attach('dispatch', new App\Security\Authenticate()); $dispatcher->setEventsManager($eventsManager); return $dispatcher; });
Свойство $accessList
позволяет быстро добавлять новые уровни доступа в приложении, например, чтобы добавить новый доступ admin
, достаточно создать класс с условием и добавить его в список $accessList
:
<?php declare(strict_types=1); namespace App\Security; use App\Security\Access\Auth; use App\Security\Access\Admin; use App\Security\Access\Guest; use Sinbadxiii\PhalconAuth\Access\Authenticate as AuthMiddleware; /** * Class Authenticate * @package App\Security */ class Authenticate extends AuthMiddleware { protected array $accessList = [ 'auth' => Auth::class, 'guest' => Guest::class, 'admin' => Admin::class, ]; }
Так же список доступов можно зарегистрировать непосредственно в Manager при создании сервис провайдера с помощью метода setAccessList()
:
$authManager = new Sinbadxiii\PhalconAuth\Manager(); $authManager->setAccessList( [ 'auth' => App\Security\Access\Auth::class, 'guest' => App\Security\Access\Guest::class, 'admin' => App\Security\Access\Admin::class, ]; ); return $authManager;
Поставщики (Providers) и Адаптеры (Adapters)
Как уже было сказано ранее поставщики определяют какие сущности будут являться пользователями, например users
или contacts
, все зависит от контекста вашего приложения.
В настоящий момент существуют три вида адаптера:
Sinbadxiii\PhalconAuth\Adapter\Model
Sinbadxiii\PhalconAuth\Adapter\Stream
Sinbadxiii\PhalconAuth\Adapter\Memory
Модель, файл и массив с данными в приложении.
Все адаптеры наследуются от абстрактного класса Sinbadxiii\PhalconAuth\Adapter\AbstractAdapter
, который имеет:
- public setModel(AuthenticatableInterface $model)` - назначить модель поставщика
- public getModel()` - получить модель поставщика
- public setConfig(array $config)` - установить конфиг
- public getConfig()` - получить конфиг адаптера
Адаптер поставщика Model
Для использования адаптера Model
нам понадобится модель пользователя, например App\Models\User::class
вида:
<?php namespace App\Models; use Phalcon\Mvc\Model; class User extends Model { public $id; public $username; public $name; public $email; public $password; public $published; public $created_at; public $updated_at; public function initialize() { $this->setSource("users"); } }
Чтобы при использовании в нашем приложении, не выдавалась ошибка:
PHP Fatal error: Uncaught TypeError: Sinbadxiii\PhalconAuth\Adapter\Model::validateCredentials(): Argument #1 ($user) must be of type Sinbadxiii\PhalconAuth\AuthenticatableInterface
.
Т.е. модель User
надо имплементировать от Sinbadxiii\PhalconAuth\AuthenticatableInterface
, а если хочется использовать возможности функции RememberMe
(Запомнить меня), то так же надо будет наследовать интерфейс Sinbadxiii\PhalconAuth\RememberingInterface
:
В конечном счете, ваша модель должна иметь класс следующего вида:
<?php namespace App\Models; use Phalcon\Encryption\Security\Random; use Phalcon\Mvc\Model; use Sinbadxiii\PhalconAuth\RememberingInterface; use Sinbadxiii\PhalconAuth\AuthenticatableInterface; use Sinbadxiii\PhalconAuth\RememberTokenInterface; class User extends Model implements AuthenticatableInterface, RememberingInterface { public $id; public $username; public $name; public $email; public $password; public $published; public $created_at; public $updated_at; public function initialize() { $this->setSource("users"); $this->hasOne( 'id', RememberToken::class, 'user_id', [ 'alias' => "remember_token" ] ); } public function setPassword(string $password) { $this->password = $this->getDI()->get("security")->hash($password); return $this; } public function getAuthIdentifier() { return $this->id; } public function getAuthPassword() { return $this->password; } public function getRememberToken(string $token = null): ?RememberTokenInterface { return $this->getRelated('remember_token', [ 'token=:token:', 'bind' => ['token' => $token] ]); } public function setRememberToken(RememberTokenInterface $value) { $this->remember_token = $value; } public function createRememberToken(): RememberTokenInterface { $random = new Random(); $token = $random->base64(60); $rememberToken = new RememberToken(); $rememberToken->token = $token; $rememberToken->user_agent = $this->getDI()->get("request")->getUserAgent(); $rememberToken->ip = $this->getDI()->get("request")->getClientAddress(); $this->setRememberToken($rememberToken); $this->save(); return $rememberToken; } }
Модель App\Models\RememberToken
будет иметь вид:
<?php declare(strict_types=1); namespace App\Models; use Phalcon\Mvc\Model; use Sinbadxiii\PhalconAuth\RememberTokenInterface; class RememberToken extends Model implements RememberTokenInterface { /** * @var integer */ public $id; /** * @var integer */ public $user_id; /** * @var string */ public $token; /** * @var string */ public $ip; /** * @var string */ public $user_agent; /** * @var integer */ public $created_at; /** * @var integer */ public $updated_at; /** * @var integer */ public $expired_at; public function initialize() { $this->setSource("users_remember_tokens"); } public function getToken(): string { return $this->token; } public function getUserAgent(): string { return $this->user_agent; } public function beforeValidationOnCreate() { $this->created_at = date(DATE_ATOM); $this->updated_at = date(DATE_ATOM); if (!$this->expired_at) { $this->expired_at = date(DATE_ATOM); } } public function beforeValidationOnSave() { if (!$this->created_at) { $this->created_at = date(DATE_ATOM); } if (!$this->expired_at) { $this->expired_at = date(DATE_ATOM); } $this->updated_at = date(DATE_ATOM); } public function beforeValidationOnUpdate() { $this->updated_at = date(DATE_ATOM); } }
Интерфейс Sinbadxiii\PhalconAuth\AuthenticatableInterface
имеет следущий вид:
<?php namespace Sinbadxiii\PhalconAuth; interface AuthenticatableInterface { public function getAuthIdentifier(); public function getAuthPassword(); }
а реализация "Запомнить меня" - Sinbadxiii\PhalconAuth\RememberingInterface
:
<?php namespace Sinbadxiii\PhalconAuth; interface RememberingInterface { public function getRememberToken(): ?RememberTokenInterface; public function createRememberToken(): RememberTokenInterface; }
Теперь можно использовать модель при создании менеджера:
use Sinbadxiii\PhalconAuth\Adapter\Model; use Sinbadxiii\PhalconAuth\Guard\Session; use Sinbadxiii\PhalconAuth\Manager; $security = $this->getSecurity(); $adapter = new Model($security); $adapter->setModel(App\Models\User::class); $guard = new Session( $adapter, $this->getSession(), $this->getCookies(), $this->getRequest(), $this->getEventsManager() ); $manager = new Manager(); $manager->addGuard("web", $guard); $manager->setDefaultGuard($guard); return $manager;
Адаптер поставщика memory
Используя setData()
можно задать массив данных с пользователями, который имеет вид:
[ ["username" =>"admin", "name" => "admin", 'password' => 'admin', "email" => "admin@admin.ru"], ["username" => "user", "name" => "user", 'password' => 'user', "email" => "user@user.ru"], ]
$di->setShared("auth", function () { $security = $this->getSecurity(); $data = [ ["auth_token" => '1', "name" => "admin", "username" => "admin", 'password' => 'admin', "email" => "admin@admin.ru"], ["auth_token" => '2', "name" => "admin1", "username" => "admin", 'password' => 'admin1', "email" => "admin1@admin.ru"], ]; $adapter = new \Sinbadxiii\PhalconAuth\Adapter\Memory($security); $adapter->setModel(App\Models\UserSimple::class); $adapter->setData($data); $configGuard = [ 'inputKey' => 'auth_token', 'storageKey' => 'auth_token', ]; $guard = new \Sinbadxiii\PhalconAuth\Guard\Token( $adapter, $configGuard, $this->getRequest() ); $manager = new Manager(); $manager->addGuard("api", $guard, true); return $manager; });
- public setData(array $data) - массив с данными
- public getData() - получить массив с данными
Адаптер поставщика Stream
Если взять в качестве адаптера поставщиков users
не Sinbadxiii\PhalconAuth\Adapter\Model
, а файл Sinbadxiii\PhalconAuth\Adapter\Stream
:
то необходимо будет задать источник файла формата json
, например, users.json
, который имеет вид:
[ {"name":"admin", "username":"admin", "password": "admin","email": "admin@admin.ru"}, {"name":"user", "username":"user", "password": "user","email": "user@user.ru"} ]
Возвращаемый пользователь в виде модели App\Models\UserSimple
должен будет реализовывать интерфейс Sinbadxiii\PhalconAuth\AuthenticatableInterface
, например:
<?php namespace App\Models; use Phalcon\Auth\AuthenticatableInterface; class UserSimple implements AuthenticatableInterface { /** * * @var integer */ public $id; /** * * @var string */ public string $username; /** * * @var string */ public string $name; /** * * @var string */ public string $email; /** * * @var string */ public string $password; /** * * @var integer */ public $published; public function __construct($data) { foreach ($data as $field => $value) { $this->$field = $value; } } /** * @return int */ public function getAuthIdentifier(): mixed { return $this->id; } /** * @return string */ public function getAuthPassword(): string { return $this->password; } }
но следует учитывать, что нельзя будет использовать функцию RememberMe
(Запомнить меня), т.к. Stream
не имплементирует интерфейс Sinbadxiii\PhalconAuth\RememberingInterface
ввиду отсутствия возможности сохранить токен сессии в файлом хранилище пользователей (что не мешает вам реализовать эту функцию в своем охраннике на основе хранилища в файле).
$security = $this->getSecurity(); $adapter = new \Sinbadxiii\PhalconAuth\Adapter\Stream($security); $adapter->setModel(App\Models\UserSimple::class); $adapter->setFileSource(__DIR__ . "/users.json"); $guard = new \Sinbadxiii\PhalconAuth\Guard\Session( $adapter, $this->getSession(), $this->getCookies(), $this->getRequest(), $this->getEventsManager() ); $manager = new Manager(); $manager->addGuard("web", $guard, true); //третий аргумент - назначить охранника по дефолту return $manager;
- public setFileSource(string $pathSrcFile) - указать путь к файлу
- public getFileSource() - получить путь к файлу
- public setData(array $data) - массив с данными пользователей
- public getData() - получить массив с данными пользователей
Не рекомендуется использовать адаптеры
stream
иmemory
в реальных приложениях из-за их функциональной ограниченности и сложности управления пользователями. Это может быть полезно в прототипах приложений и для ограниченных приложений, которые не хранят пользователей в базах данных.
Создание своего адаптера поставщика
Интерфейс адаптера поставщика Sinbadxiii\PhalconAuth\Adapter\AdapterInterface
имеет вид:
<?php namespace Sinbadxiii\PhalconAuth\Adapter; use Sinbadxiii\PhalconAuth\AuthenticatableInterface; interface AdapterInterface { public function findFirstByCredentials(array $credentials); public function findFirstById($id); public function validateCredentials(AuthenticatableInterface $user, array $credentials): bool; }
Так же для создания функционала "Запомнить меня" нужна реализация интерфейса Sinbadxiii\PhalconAuth\Adapter\AdapterWithRememberTokenInterface
:
<?php declare(strict_types=1); namespace Sinbadxiii\PhalconAuth\Adapter; use Sinbadxiii\PhalconAuth\AuthenticatableInterface; use Sinbadxiii\PhalconAuth\RememberingInterface; use Sinbadxiii\PhalconAuth\RememberTokenInterface; interface AdapterWithRememberTokenInterface { public function findFirstByToken($identifier, $token, $user_agent): ?AuthenticatableInterface; public function createRememberToken(RememberingInterface $user): RememberTokenInterface; }
Manager Factory
Sinbadxiii\PhalconAuth\MangerFactory
- это создание менеджера аутентификации с минимальными усилиями, если вы не хотите настраивать вручную менеджер аутентификации, а хотите быстро запустить сервис провайдер аутентификации, вы можете сделать это так:
$di->setShared('auth', function () { $manager = new \Sinbadxiii\PhalconAuth\ManagerFactory(); return $manager; });
Все, дальше ManagerFactory
сделает все за вас, на основе вашего конфигурационного файла. По умолчанию используется конфигурация из $this->config->auth
, если вы хотите использовать другую конфигурацию, отличную от $this->config->auth
то можно передать в качестве первого аргумента другой конфиг:
$di->setShared("auth", function () { $config = $this->getConfig()->auth_config_other; $manager = new \Sinbadxiii\PhalconAuth\ManagerFactory($config->toArray()); return $manager; });
Пример конфигурационного файла для использования Сессий
Итак, типичный пример конфигурационного файла приложения на основе Сессий. Файл может находится в папке конфигов config/auth.php
или в глобальном файле config.php
с доступом по ключу auth
($this->config->auth
).
<?php [ 'auth' => [ 'defaults' => [ //дефолтные значения 'guard' => 'web' //дефолтный охранник ], 'guards' => [ //список охранников 'web' => [ 'driver' => 'session', //драйвер сессия 'provider' => 'users', //поставщики users ], ], 'providers' => [ 'users' => [ 'adapter' => 'model', //адаптер поставщика users - model 'model' => App\Models\User::class, //модель ] ] ], ];
Т.е. приложение будет использовать guard = web
. В свою очередь Охранник web основан на драйвере session
и использует поставщика пользователей users
, которые извлекаются через Адаптера model
- App\Models\Users
.
Данный конфигурационный файл позволяет создавать различные комбинации охранников и поставщиков, разделяя доступы в вашем приложений.
Пример конфигурационного файла для использования Токена
<?php [ 'auth' => [ 'defaults' => [ 'guard' => 'api' ], 'guards' => [ 'api' => [ 'driver' => 'token', 'provider' => 'users', 'inputKey' => 'auth_token', //опционально, по дефолту auth_token 'storageKey' => 'auth_token', //опционально, по дефолту auth_token ], ], 'providers' => [ 'users' => [ 'adapter' => 'model', 'model' => App\Models\User::class, ] ] ], ];
Расширить охранников можно с помощью extendGuard
и передать в качестве аргумента имя охранника, используемого в конфиге jwt
, а так же Closure
с передачей аргументов в новый класс охранника, например:
$di->setShared('auth', function () { $auth = new \Sinbadxiii\PhalconAuth\ManagerFactory(); $request = $this->getRequest(); $manager->extendGuard("jwt", function ($adapter, $config) use ($request) { return new JwtGuard($adapter, $config, $request); }); return $auth; });
Вы можете расширить список адаптеров с помощью метода extendProviderAdapter
, например:
$di->setShared("auth", function () { $authManager = new Phalcon\Auth\ManagerFactory(); $authManager->extendProviderAdapter("mongo", function($security, $configProvider) { return new App\Security\Adapter\Mongo($security, $configProvider); }); return $authManager; });
Методы
Задать требуемый доступ к контроллеру
Метод access()
позволит задать требуемый доступ к контроллеру, из коробки auth
- для аутентифицированных, guest
- для гостей.
$this->auth->access("auth")
<?php declare(strict_types=1); namespace App\Controllers; class ProfileController extends ControllerBase { public function onConstruct() { $this->auth->access("auth"); } public function indexAction() { } }
Проверка аутентификации текущего пользователя
Чтобы определить, аутентифицирован ли пользователь, выполняющий входящий HTTP-запрос, вы можете использовать метод check()
. Этот метод вернет true, если пользователь аутентифицирован:
$this->auth->check(); //check authentication
Например, вы можете проверить на странице формы входа, что если пользователь вошел в систему, то не показывать ему форму ввода:
public function loginFormAction() { if ($this->auth->check()) { //redirect to profile page return $this->response->redirect( "/profile", true ); } }
Получение аутентифицированного пользователя
При обработке входящего запроса вы можете получить доступ к аутентифицированному пользователю с помощью метода user()
. Результатом будет провайдер, указанный в конфигурации config->auth
, в соответствии с интерфейсом Sinbadxiii\PhalconAuth\AuthenticatableInterface
.
Вы также можете запросить идентификатор пользователя (ID) с помощью метода id()
:
$this->auth->user(); //get the user $this->auth->id(); //get user id
Попытка аутентификации
Метод attempt()
используется для обработки попыток аутентификации из формы входа в ваше приложение:
$username = $this->request->getPost("username"); $password = $this->request->getPost("password"); //attempt login with credentials if ($this->auth->attempt(['username' => $username, 'password' => $password])) { //success attempt ... } //fail attempt
Метод attempt()
принимает в качестве первого аргумента массив пар ключ/значение. Значения в массиве будут использоваться для поиска пользователя в таблице базы данных пользователей. Итак, в приведенном выше примере пользователь будет получен по значению столбца имени пользователя. Если пользователь найден, хешированный пароль, хранящийся в базе данных, будет сравниваться со значением пароля, переданным методу. Вы не должны хешировать значение входящего запроса пароля, так как пароль уже автоматически хэшируется, чтобы сравнить его с хешированным паролем в базе данных. Аутентифицированный сеанс будет запущен для пользователя, если хешированные пароли совпадают.
Помните, что пользователи из вашей базы данных будут запрашиваться на основе конфигурации «поставщика».
Метод attempt()
вернет true
, если аутентификация прошла успешно. В противном случае будет возвращено false
.
Указание дополнительных учетных данных
Вы также можете добавить запрос дополнительных данные в дополнение к электронной почте/имени пользователя и паролю. Для этого просто добавьте условия запроса в массив, переданный методу attempt()
. Например, мы можем проверить, является ли пользователь опубликованным is_published
:
$username = $this->request->getPost("username"); $password = $this->request->getPost("password"); //attempt login with additional credentials if ($this->auth->attempt(['username' => $username, 'password' => $password, 'is_published' => 1])) { //success attempt ... } //fail attempt
"Запомнить меня"
Если вы хотите обеспечить функциональность «запомнить меня» в своем приложении, вы можете передать логическое значение в качестве второго аргумента метода попытки.
Если это значение равно true
, пользователь будет аутентифицироваться на неопределенный срок или до тех пор, пока пользователь не выйдет из системы вручную с помощью logout()
. Таблица users_remember_tokens
содержит столбец строки токена, который будет использоваться для хранения токена «запомнить меня»:
$username = $this->request->getPost("username"); $password = $this->request->getPost("password"); $remember = this->request->getPost('remember') ? true : false; //attempt login with credentials and remember if ($this->auth->attempt(['username' => $username, 'password' => $password], $remember)) { //success attempt ... } //fail attempt
Используйте метод viaRemember()
, чтобы проверить, аутентифицирован ли пользователь с помощью файла cookie «запомнить меня»:
//use method viaRemember to check the user was authenticated using the remember me cookie $this->auth->viaRemember();
Аутентифицировать пользовательский экземпляр
Если вам нужно установить существующий пользовательский экземпляр в качестве текущего аутентифицированного пользователя, вы можете передать пользовательский экземпляр методу login()
. Данный пользовательский экземпляр должен быть реализацией Sinbadxiii\PhalconAuth\AuthenticatableInterface
.
Этот метод аутентификации полезен, когда у вас уже есть действующий экземпляр пользователя, например, сразу после регистрации пользователя в вашем приложении:
$user = Users::findFirst(1); // Login and Remember the given user $this->auth->login($user, $remember = true);
Аутентифицировать пользователя по идентификатору
Для аутентификации пользователя с использованием первичного ключа записи в базе данных вы можете использовать метод loginById()
. Этот метод принимает первичный ключ пользователя, которого вы хотите аутентифицировать:
//and force login user by id $this->auth->loginById(1, true);
Аутентифицировать пользователя один раз
Используя метод once()
вы можете аутентифицировать пользователя в приложении для одного запроса. При вызове этого метода не будут использоваться сессии или файлы cookie:
//once auth without saving session and cookies $username = $this->request->getPost("username"); $password = $this->request->getPost("password"); $this->auth->once(['username' => $username, 'password' => $password]);
Выход
Чтобы вручную разлогинить пользователя из вашего приложения, вы можете использовать метод logout()
. После этого удалится вся информация об аутентификации из сессии пользователя, так что последующие запросы уже не будут аутентифицированы.
$this->auth->logout(); //log out user
HTTP Basic Authentication
Базовая аутентификация HTTP обеспечивает быстрый способ аутентификации пользователей вашего приложения без настройки специальной страницы «входа в систему». Достаточно передать в заголовке Authorization
, значение Basic
и пары емейл (либо другое поле пользователя) и пароль, разделенные двоеточием и закодированые base64_encode()
Метод $this->auth->basic("email")
позволит создать свой Access для использования доступа с помощью Auth Basic.
Аргумент email
указывает на то, что поиск пользователя будет осуществляться по полям email и password. Указав другое поле, например username
, поиск будет осуществляться по паре username
и password
:
<?php namespace App\Security\Access; use Sinbadxiii\PhalconAuth\Access\AbstractAccess; use Sinbadxiii\PhalconAuth\Exception; class AuthWithBasic extends AbstractAccess { /** * @return bool */ public function allowedIf(): bool { if ($this->auth->basic("email")) { return true; } return false; } /** * @return void * @throws Exception */ public function redirectTo() { throw new Exception("Basic: Invalid credentials."); } }
После запроса, в сессию запишется пользователь, и последующие запросы, могут уже не содержать пользовательские данные в заголовке Authorization
, до тех пор пока сессия не "стухнет".
Basic HTTP-аутентификация без сохранения состояния
Вы можете использовать базовую аутентификацию HTTP без сохранения пользователя в сессии. Это в первую очередь полезно, если вы решите использовать HTTP-аутентификацию для аутентификации запросов к API вашего приложения. Для этого можно создать Access, который вызывает метод onceBasic()
, например:
<?php namespace App\Security\Access; use Sinbadxiii\PhalconAuth\Access\AbstractAccess; use Sinbadxiii\PhalconAuth\Exception; class AuthWithBasic extends AbstractAccess { /** * @return bool */ public function allowedIf(): bool { if ($this->auth->onceBasic("email")) { return true; } return false; } /** * @return void * @throws Exception */ public function redirectTo() { throw new Exception("Basic: Invalid credentials."); } }
После запроса, ни куки, ни сессия не будут содержать данные о пользователе, и следущий запрос так же должен содержать пользовательские данные заголовка Authorization
, иначе будет вызвано исключение Sinbadxiii\PhalconAuth\Exceptions
Events
License
The MIT License (MIT). Please see License File for more information.