Skip to content

Instantly share code, notes, and snippets.

@rogatec
Created August 25, 2016 07:30
Show Gist options
  • Save rogatec/6ba7bcd418012193747cdaf7d870209c to your computer and use it in GitHub Desktop.
Save rogatec/6ba7bcd418012193747cdaf7d870209c to your computer and use it in GitHub Desktop.
Zend Framework 3 Authentication with Session setup
<?php
namespace Authentication\Controller;
use Employee\Model\EmployeeTable;
use Main\Form\LoginForm;
use Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Db\Sql\Select;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Mvc\Plugin\FlashMessenger\FlashMessenger;
class AuthController extends AbstractActionController
{
/**
* @var AuthenticationService
*/
private $authService;
/**
* @var EmployeeTable
*/
private $employeeTable;
/**
* AuthController constructor.
*
* @param AuthenticationService $authService
* @param EmployeeTable $employeeTable
*/
public function __construct(AuthenticationService $authService, EmployeeTable $employeeTable)
{
$this->authService = $authService;
$this->employeeTable = $employeeTable;
}
/**
* @return ViewModel
*/
public function loginAction()
{
$form = new LoginForm();
$request = $this->getRequest();
if (!$request->isPost()) {
return ['form' => $form];
}
$form->setData($request->getPost());
if (!$form->isValid()) {
$this->flashMessenger()->addMessage("your error message", FlashMessenger::NAMESPACE_ERROR);
return ['form' => $form];
}
/** @var Employee $employeeObject */
$employeeObject = $this->employeeTable->getEmployeeByLogin($request->getPost('system_name'), $request->getPost('password'));
if (!$employeeObject) {
$this->flashMessenger()->addMessage("your error message", FlashMessenger::NAMESPACE_ERROR);
return ['form' => $form];
}
if ($this->authenticate($employeeObject)) {
return $this->redirect()->toRoute('main');
}
$this->flashMessenger()->addMessage("your error message", FlashMessenger::NAMESPACE_ERROR);
return ['form' => $form];
}
public function logoutAction()
{
$this->authService->clearIdentity();
return $this->redirect()->toRoute('login');
}
/**
* @param $employeeObject
*
* @return array
*/
private function authenticate($employeeObject)
{
/** @var CredentialTreatmentAdapter $adapter */
$adapter = $this->authService->getAdapter();
$select = $adapter->getDbSelect();
$select->join(['R' => 'role'], 'role_id = R.id', ['role' => 'description'], Select::JOIN_LEFT);
$this->authService->setAdapter($adapter);
$this->authService->getAdapter()->setIdentity($employeeObject->system_name)->setCredential($employeeObject->password);
$result = $this->authService->authenticate();
if ($result->isValid()) {
$resultRow = $this->authService->getAdapter()->getResultRowObject();
$this->authService->getStorage()->write([
'id' => $resultRow->id,
'system_name' => $resultRow->system_name,
'role' => $resultRow->role,
]);
return true;
}
return false;
}
}
<?php
namespace Authentication\Factory;
use Employee\Model\EmployeeTable;
use Interop\Container\ContainerInterface;
use Interop\Container\Exception\ContainerException;
use Authentication\Controller\AuthController;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\FactoryInterface;
class AuthControllerFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
*
* @return object
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new AuthController($container->get(AuthenticationService::class), $container->get(EmployeeTable::class));
}
}
<?php
namespace Authentication\Factory;
use Authentication\Storage\AuthStorage;
use Interop\Container\ContainerInterface;
use Interop\Container\Exception\ContainerException;
use Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Db\Adapter\AdapterInterface;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\FactoryInterface;
class AuthenticationServiceFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
*
* @return object
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$dbAdapter = $container->get(AdapterInterface::class);
$authAdapter = new CredentialTreatmentAdapter($dbAdapter, 'employee', 'system_name', 'password');
return new AuthenticationService($container->get(AuthStorage::class), $authAdapter);
}
}
<?php
namespace Authentication\Storage;
use Zend\Authentication\Storage\Session;
use Zend\Authentication\Storage\StorageInterface;
class AuthStorage implements StorageInterface
{
const NAME = 'your_session_name';
/**
* @var StorageInterface
*/
private $storage;
/**
* @var mixed
*/
private $resolvedIdentity;
/**
* Returns true if and only if storage is empty
*
* @throws \Zend\Authentication\Exception\ExceptionInterface If it is impossible to determine whether storage is empty
* @return bool
*/
public function isEmpty()
{
if ($this->getStorage()->isEmpty()) {
return true;
}
$identity = $this->getStorage()->read();
if ($identity === null) {
$this->clear();
return true;
}
return false;
}
/**
* Returns the contents of storage
*
* Behavior is undefined when storage is empty.
*
* @throws \Zend\Authentication\Exception\ExceptionInterface If reading contents from storage is impossible
* @return mixed
*/
public function read()
{
if ($this->resolvedIdentity !== null) {
return $this->resolvedIdentity;
}
$identity = $this->getStorage()->read();
if ($identity) {
$this->resolvedIdentity = $identity;
} else {
$this->resolvedIdentity = null;
}
return $this->resolvedIdentity;
}
/**
* Writes $contents to storage
*
* @param mixed $contents
*
* @throws \Zend\Authentication\Exception\ExceptionInterface If writing $contents to storage is impossible
* @return void
*/
public function write($contents)
{
$this->resolvedIdentity = null;
$this->getStorage()->write($contents);
}
/**
* Clears contents from storage
*
* @throws \Zend\Authentication\Exception\ExceptionInterface If clearing contents from storage is impossible
* @return void
*/
public function clear()
{
$this->resolvedIdentity = null;
$this->getStorage()->clear();
}
/**
* @param StorageInterface $storage
*
* @return AuthStorage $this
*/
public function setStorage(StorageInterface $storage)
{
$this->storage = $storage;
return $this;
}
/**
* @return StorageInterface
*/
public function getStorage()
{
if ($this->storage === null) {
$this->setStorage(new Session(self::NAME));
}
return $this->storage;
}
}
<?php
use Authentication\Controller\AuthController;
use Zend\Router\Http\Literal;
return [
'router' => [
'routes' => [
'login' => [
'type' => Literal::class,
'options' => [
'route' => '/login',
'defaults' => [
'controller' => AuthController::class,
'action' => 'login',
],
],
],
'logout' => [
'type' => Literal::class,
'options' => [
'route' => '/logout',
'defaults' => [
'controller' => AuthController::class,
'action' => 'logout',
],
],
],
],
],
'controllers' => [
'factories' => [
\Authentication\Controller\AuthController::class => \Authentication\Factory\AuthControllerFactory::class,
],
],
'service_manager' => [
'factories' => [
\Authentication\Storage\AuthStorage::class => \Zend\ServiceManager\Factory\InvokableFactory::class,
\Zend\Authentication\AuthenticationService::class => \Authentication\Factory\AuthenticationServiceFactory::class,
],
],
'view_manager' => [
'template_path_stack' => [
'Authentication' => __DIR__ . '/../view',
],
],
];
<?php
// located at $PROJECT_ROOT/config/autoload
use Zend\Db\Adapter\AdapterAbstractServiceFactory;
/**
* configurations across all modules - specify sensitive data and personal settings in your local.php
*/
return [
'session_validators' => [
\Zend\Session\Validator\RemoteAddr::class,
\Zend\Session\Validator\HttpUserAgent::class,
],
'session_config' => [
'remember_me_seconds' => 604800, // one week
'use_cookies' => true,
'cookie_lifetime' => 604800, // one week
'name' => 'your_session_name',
],
'session_storage' => [
'type' => \Zend\Session\Storage\SessionArrayStorage::class,
],
];
<?php
namespace Main;
use Main\Factory\SessionManagerFactory;
use Zend\Session\SessionManager;
return [
'service_manager' => [
'factories' => [
SessionManager::class => SessionManagerFactory::class,
],
],
];
<?php
namespace Main;
use Zend\Authentication\AuthenticationService;
use Zend\Http\Request;
use Zend\Mvc\MvcEvent;
use Zend\Session\Container;
use Zend\Session\SessionManager;
use Zend\Session\Validator\HttpUserAgent;
use Zend\Session\Validator\RemoteAddr;
class Module
{
/**
* @var AuthenticationService
*/
private $auth;
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
public function onBootstrap(MvcEvent $mvcEvent)
{
$this->bootstrapSession($mvcEvent);
$this->auth = $mvcEvent->getApplication()->getServiceManager()->get(AuthenticationService::class);
// store user and role in global viewmodel
if ($this->auth->hasIdentity()) {
// for e.g. store your auth identity into ViewModel
$mvcEvent->getViewModel()->setVariable('authIdentity', $this->auth->getIdentity());
// extend functionality with acl to checkPermission if user has rights to the given route
// ...
} else {
// redirect if auth fails for example back to /login
// ..
}
}
/**
* @param MvcEvent $e
*/
private function bootstrapSession($e)
{
/** @var SessionManager $session */
$session = $e->getApplication()->getServiceManager()->get(SessionManager::class);
$session->start();
$container = new Container('your_session_name', $session);
if (isset($container->init)) {
return;
}
/** @var Request $request */
$request = $e->getRequest();
$session->regenerateId(true);
$container->init = 1;
$container->remoteAddr = $request->getServer()->get('REMOTE_ADDR');
$container->httpUserAgent = $request->getServer()->get('HTTP_USER_AGENT');
$config = $e->getApplication()->getServiceManager()->get('config');
if (!isset($config['session'])) {
return;
}
if (!isset($config['session_validators'])) {
return;
}
$chain = $session->getValidatorChain();
foreach ($config['session_validators'] as $validator) {
switch ($validator) {
case HttpUserAgent::class:
$validator = new $validator($container->httpUserAgent);
break;
case RemoteAddr::class:
$validator = new $validator($container->remoteAddr);
break;
default:
$validator = new $validator();
}
$chain->attach('session.validate', [$validator, 'isValid']);
}
}
}
<?php
namespace Main\Factory;
use Interop\Container\ContainerInterface;
use Interop\Container\Exception\ContainerException;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\Session\Config\SessionConfig;
use Zend\Session\Container;
use Zend\Session\SessionManager;
class SessionManagerFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
*
* @return object
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$config = $container->get('config');
if (!isset($config['session_config'])) {
$sessionManager = new SessionManager();
Container::setDefaultManager($sessionManager);
return $sessionManager;
}
// create session config if exists in global configuration
$sessionConfig = null;
if (isset($config['session_config'])) {
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session_config']);
}
// create session storage if exists in global configuration
$sessionStorage = null;
if (isset($config['session_storage'])) {
$class = $config['session_storage']['type'];
$sessionStorage = new $class('hpv');
}
// optional get a save handler and store it into SessionManager (currently null)
$sessionManager = new SessionManager(
$sessionConfig,
$sessionStorage,
null
);
Container::setDefaultManager($sessionManager);
return $sessionManager;
}
}
@lmcosorio
Copy link

Hi,
I just want to thank you for the great work. Helped me a lot!

Luis

@dccupp
Copy link

dccupp commented May 30, 2019

I can't begin to tell you how much this helped me. Thank you so much for posting this.

Dennis

@jcroot
Copy link

jcroot commented Feb 28, 2020

Thanks for sharing this, if very useful for people like me, beginner into laminas (zf3). I have lot of experience with zf1, is hard to migrate to zf3..

@ductu1411
Copy link

Thank you for the great work!

@anjalbinayak
Copy link

Great Work

@freemiumdev
Copy link

thanks great work!!
I'm starting using Laminas 20 days ago, and i try implement your solution on mvc skeleton, but i get this error :

Fatal error: Uncaught Laminas\ServiceManager\Exception\ServiceNotFoundException: Unable to resolve service "Laminas\Authentication\AuthenticationService" to a factory; are you certain you provided it during configuration?

on Line 29 of Module.php of Main (in my app is Application) module.
can you help me?

@rogatec
Copy link
Author

rogatec commented Apr 15, 2021

thanks great work!!
I'm starting using Laminas 20 days ago, and i try implement your solution on mvc skeleton, but i get this error :

Fatal error: Uncaught Laminas\ServiceManager\Exception\ServiceNotFoundException: Unable to resolve service "Laminas\Authentication\AuthenticationService" to a factory; are you certain you provided it during configuration?

on Line 29 of Module.php of Main (in my app is Application) module.
can you help me?

Hi 🙋, well to be honest I don’t develop anymore with Zend (didn’t know it’s called Laminas now).
But this error looks like, that the AuthenticationService is missing (originally in Zend\Authentication\AuthenticationService).
Take a look at the documentation - 🤞🏽

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment