ZF3 MVC Zend\Аутентификация как фабрика услуг

Я пытаюсь адаптировать свой модуль ZF2 User к ZF3 MVC. У него есть менеджер службы аутентификации, который вызывается в функции onBootsrap внутри класса Module для каждого запроса (т. е. загрузки страницы), чтобы проверить, аутентифицирован ли пользователь.

Поскольку serviceLocator и ServiceAware больше не доступны, я пытаюсь создать AuthenticationServiceFactory, но пока не получается. У вас есть какие-либо идеи о том, что я делаю неправильно и как я могу сделать это с ZF3?

Вот упрощенная версия моего файла module/User/config.module.config.php

namespace User;

use ...

return [
    'router' => [...],
    'controllers' => [...],
    'service_manager' => [
        'factories' => [
            Service\AuthenticationServiceFactory::class => InvokableFactory::class,
        ],
    ],
];

Вот мой файл module/User/src/Service/AuthenticationServiceFactory.php

namespace User\Service;

use Interop\Container\ContainerInterface;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\Db\Adapter\Adapter as DbAdapter;
use Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter as AuthAdapter;
use Zend\Authentication\Storage\Session as Storage;

class AuthenticationServiceFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $controllerPluginManager = $container;
        $serviceManager = $controllerPluginManager->get('ServiceManager');
        $config = $serviceManager->get('configuration');

        $dbAdapter = new DbAdapter($config['db']); // Mysqli driver working in other modules

        $authAdapter = new AuthAdapter($dbAdapter);
        $authAdapter->setTableName('user')->setIdentityColumn('username')->setCredentialColumn('password');

        $storage = new Storage();

        return new AuthenticationService($storage, $authAdapter);
    }
}

Вот мой файл module/User/src/Module.php

namespace User\Service;

use Zend\Mvc\MvcEvent;
use Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter;

class Module
{
    public function getConfig()
    {
        return include __DIR__ . '/../config/module.config.php';
    }

    public function onBootstrap(MvcEvent $e)
    {
        $services = $e->getApplication()->getServiceManager();
        $auth = $services->get(AuthenticationServiceFactory::class);

        // Returns Fatal error: Call to undefined method Zend\Authentication\AuthenticationServiceFactory::setIdentity()
        // $auth is an AuthenticationServiceFactory object and not the AuthenticationService returned by its __invoke() function  
        $this->authAdapter->setIdentity('dummy_user_name');
        $this->authAdapter->setCredential('dummy_password');
        print_r($this->authAdapter->authenticate());
    }
}

Любые идеи ?


person l_r    schedule 31.08.2016    source источник
comment
У меня есть модуль ACL/Auth, написанный здесь, который может помочь вам с идеями о слушателях и стратегиях событий github.com/Saeven/zf3-circlical-user   -  person Saeven    schedule 09.10.2016


Ответы (2)


Как обычно, когда я нахожу ответ самостоятельно, я размещаю его здесь, на случай, если он может кому-то помочь.

module/User/config.module.php

namespace User;

use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;

return [
    'router' => [...],
    'controllers' => [...],
    'service_manager' => [
        'factories' => [
            'auth-service' => Service\AuthenticationServiceFactory::class,
        ],
    ],
];

module/User/src/Service/AuthenticationServiceFactory.php

namespace User\Service;

use Interop\Container\ContainerInterface;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\Db\Adapter\Adapter as DbAdapter;
use Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter as AuthAdapter;
use Zend\Authentication\Storage\Session as Storage;

class AuthenticationServiceFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        // Get data from config files.
        $controllerPluginManager = $container;
        $serviceManager = $controllerPluginManager->get('ServiceManager');
        $config = $serviceManager->get('configuration');
        // or we can simplify these 3 lines using the following
        //$config = $container->get('configuration');

        // Configure DbAdapter with set-up information from config files.
        $dbAdapter = new DbAdapter($config['db']); // Mysqli driver working in other modules

        // Configure AuthAdapter with DbAdapter.
        // See https://docs.zendframework.com/zend-authentication/adapter/dbtable/credential-treatment/
        $authAdapter = new AuthAdapter($dbAdapter);
        $authAdapter->setTableName('user')->setIdentityColumn('username')->setCredentialColumn('password');

        // Configure session storage.
        $storage = new Storage();

        // Return AuthenticationService.
        return new AuthenticationService($storage, $authAdapter);
    }
}

module/User/src/Module.php

namespace User\Service;

use Zend\Mvc\MvcEvent;
use Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter;

class Module
{
    public function getConfig()
    {
        return include __DIR__ . '/../config/module.config.php';
    }

    public function onBootstrap(MvcEvent $e)
    {
        // Get the service manager.
        $services = $e->getApplication()->getServiceManager();

        // Set event to retrieve user's identity for every request.
        $eventManager = $e->getApplication()->getEventManager();
        $eventManager->attach(MvcEvent::EVENT_ROUTE, array($this, 'protectPage'), -100);
    }

    public function protectPage(MvcEvent $event)
    {
        $match = $event->getRouteMatch();
        if (! $match) {
            // We cannot do anything without a resolved route.
            return;
        }

        // Get AuthenticationService and do the verification.
        $services = $event->getApplication()->getServiceManager();
        $authService = $services->get('auth-service');

        // If user does not have an identity yet.
        if (! $authService->hasIdentity()) {
            // Do what you want like routing to login page...
        }
    }
}

Моя проблема заключалась в том, что установка идентификатора и учетных данных пользователя должна была выполняться не здесь, а в функции login() где-то еще. В Module Class нам просто нужно проверить личность.

person l_r    schedule 02.09.2016

Я проголосовал за ваш собственный ответ, потому что считаю его хорошим, но, возможно, это улучшит его для связи с БД.

Вы написали, что у вас уже есть драйвер Mysqli, работающий с другим модулем. Если у вас есть репозиторий в этом другом модуле для пользовательской таблицы, вы можете использовать его и упростить свой код с помощью специального адаптера. Предположим, что ваш пользовательский репозиторий реализует User\Model\UserRepositoryInterface:

namespace User\Model;

interface UserRepositoryInterface
{
    public function getUser($id);
    public function updateUser(User $user);
    // other methods...
}

Module\src\Factory\CustomAdapterFactory.php

namespace User\Factory;

use Interop\Container\ContainerInterface;
use User\Adapter\CustomAdapter;
use User\Model\UserRepositoryInterface;
use Zend\ServiceManager\Factory\FactoryInterface;

class CustomAdapterFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        return new CustomAdapter($container->get(UserRepositoryInterface::class));
    }
}

Ваш AuthenticationServiceFactory становится:

namespace User\Factory;

use Interop\Container\ContainerInterface;
use User\Adapter\CustomAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\Authentication\Storage\Session as SessionStorage;

class AuthenticationServiceFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $authService = new AuthenticationService();
        $storage = new SessionStorage();
        $authService->setStorage($storage);
        $authService->setAdapter($container->get(CustomAdapter::class));
        return $authService;
    }
}

Зарегистрируйте свои фабрики: module/User/config.module.php

namespace User;

use User\Factory\AuthenticationServiceFactory;
use User\Factory\CustomAdapterFactory;
use User\Factory\UserRepositoryFactory;
use Zend\Authentication\AuthenticationService;

return [
    'service_manager' => [
        'aliases' => [
            Model\UserRepositoryInterface::class => Model\UserRepository::class
        ],
        'factories' => [
            Model\UserRepository::class => UserRepositoryFactory::class,
            Adapter\CustomAdapter::class => CustomAdapterFactory::class,
            MailService::class => MailServiceFactory::class,
            AuthenticationService::class => AuthenticationServiceFactory::class,
        ]
    ],
    // etc...
];
person Al Foиce ѫ    schedule 05.09.2016
comment
Вы правы, это хорошее предложение минимизировать избыточность и, таким образом, сократить обслуживание. - person l_r; 10.09.2016