ZF3 MVC Zend\Authentication 作为服务工厂
ZF3 MVC Zend\Authentication as a Service Factory
我正在尝试使我的 ZF2 User
模块适应 ZF3 MVC。它有一个身份验证服务管理器,在 Module
class 中的 onBootsrap
函数中为每个请求(即页面加载)调用以检查用户是否已通过身份验证。
由于 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());
}
}
有什么想法吗?
像往常一样,当我自己找到答案时,我post在这里,以防它可能对某人有所帮助。
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
中我们只需要检查身份。
我对你自己的回答投了赞成票,因为我认为这是一个很好的答案,但也许这会改善数据库连接。
您写道您已经有一个与另一个模块一起工作的 Mysqli 驱动程序。如果您在这个其他模块中有用户 table 的存储库,您可以使用它并使用自定义适配器简化代码。假设您的用户存储库实现 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...
];
我正在尝试使我的 ZF2 User
模块适应 ZF3 MVC。它有一个身份验证服务管理器,在 Module
class 中的 onBootsrap
函数中为每个请求(即页面加载)调用以检查用户是否已通过身份验证。
由于 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());
}
}
有什么想法吗?
像往常一样,当我自己找到答案时,我post在这里,以防它可能对某人有所帮助。
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
中我们只需要检查身份。
我对你自己的回答投了赞成票,因为我认为这是一个很好的答案,但也许这会改善数据库连接。
您写道您已经有一个与另一个模块一起工作的 Mysqli 驱动程序。如果您在这个其他模块中有用户 table 的存储库,您可以使用它并使用自定义适配器简化代码。假设您的用户存储库实现 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...
];