Php 重构对每个Zf2控制器操作的一些调用
我需要做一个自定义的Php 重构对每个Zf2控制器操作的一些调用,php,architecture,zend-framework2,refactoring,Php,Architecture,Zend Framework2,Refactoring,我需要做一个自定义的isgrated方法(不使用来自社区的Rbac或acl模块)。因此,我有一个提供功能的服务。但是这个代码: if (!$this->userService->isGrantedCustom($this->session->offsetGet('cod_lvl'), 'ZF_INV_HOM')) { throw new \Exception("you_are_not_allowed", 1); } …在我的每个控制器和每个操作中都是重复的。参数
isgrated
方法(不使用来自社区的Rbac或acl模块)。因此,我有一个提供功能的服务。但是这个代码:
if (!$this->userService->isGrantedCustom($this->session->offsetGet('cod_lvl'), 'ZF_INV_HOM')) {
throw new \Exception("you_are_not_allowed", 1);
}
…在我的每个控制器和每个操作中都是重复的。参数的变化当然取决于权限('ZF_INV_HOM'
,'ZF_TODO_DELETE'
…)
我认为在调用控制器之前编写这些代码不是一个坏主意,但我不知道什么是最佳解决方案(最佳体系结构),以及如何将这些参数传递给它(我考虑了控制器上的注释,但如何处理?)
关键是,如果我必须修改此代码,我无法想象要修改数百次,对于每个控制器,我的每个操作,我都需要将此代码放在一个位置。通过将事件侦听器附加到SharedEventManager,您可以将所有控制器作为目标,并在一个位置进行授权检查 在这种情况下,目标是
Zend\Mvc\Controller\AbstractActionController
,这意味着任何扩展它的控制器都将执行侦听器。此侦听器的高优先级意味着它在目标控制器操作之前执行,从而使您有机会处理任何未经授权的请求
public function onBootstrap(MvcEvent $event)
{
$application = $event->getApplication();
$eventManager = $application->getEventManager()->getSharedManager();
$eventManager->attach(
\Zend\Mvc\Controller\AbstractActionController::class, // Identity of the target controller
MvcEvent::EVENT_DISPATCH,
[$this, 'isAllowed'],
1000 // high priority
);
}
在每个控制器中,都需要某种方式来确定正在访问哪个“资源”
例如,它可以实现这个接口
interface ResourceInterface
{
// Return a unique key representing the resource
public function getResourceId();
}
然后,侦听器可能看起来像这样
public function isAllowed(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
// We need the 'current' user identity
$authService = $serviceManager->get('Zend\Authentication\AuthenticationService');
$identity = $authService->getIdentity();
// The service that performs the authorization
$userService = $serviceManager->get('MyModule\Service\UserService');
// The target controller is itself a resource (the thing we want to access)
// in this example it returns an resource id so we know what we want to access
// but you could also get this 'id' from the request or config etc
$controller = $event->getTarget();
if ($controller instanceof ResourceInterface) {
$resourceName = $controller->getResourceId();
// Test the authorization, is UserX allowed resource ID Y
if (empty($resourceName) || $userService->isGrantedCustom($identity, $resourceName)) {
// early exit for success
return;
} else {
// Denied; perhaps trigger a new custom event or return a response
}
}
}
通过将事件侦听器附加到SharedEventManager,您可以将所有控制器作为目标,并在一个位置进行授权检查 在这种情况下,目标是
Zend\Mvc\Controller\AbstractActionController
,这意味着任何扩展它的控制器都将执行侦听器。此侦听器的高优先级意味着它在目标控制器操作之前执行,从而使您有机会处理任何未经授权的请求
public function onBootstrap(MvcEvent $event)
{
$application = $event->getApplication();
$eventManager = $application->getEventManager()->getSharedManager();
$eventManager->attach(
\Zend\Mvc\Controller\AbstractActionController::class, // Identity of the target controller
MvcEvent::EVENT_DISPATCH,
[$this, 'isAllowed'],
1000 // high priority
);
}
在每个控制器中,都需要某种方式来确定正在访问哪个“资源”
例如,它可以实现这个接口
interface ResourceInterface
{
// Return a unique key representing the resource
public function getResourceId();
}
然后,侦听器可能看起来像这样
public function isAllowed(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
// We need the 'current' user identity
$authService = $serviceManager->get('Zend\Authentication\AuthenticationService');
$identity = $authService->getIdentity();
// The service that performs the authorization
$userService = $serviceManager->get('MyModule\Service\UserService');
// The target controller is itself a resource (the thing we want to access)
// in this example it returns an resource id so we know what we want to access
// but you could also get this 'id' from the request or config etc
$controller = $event->getTarget();
if ($controller instanceof ResourceInterface) {
$resourceName = $controller->getResourceId();
// Test the authorization, is UserX allowed resource ID Y
if (empty($resourceName) || $userService->isGrantedCustom($identity, $resourceName)) {
// early exit for success
return;
} else {
// Denied; perhaps trigger a new custom event or return a response
}
}
}
如果您不想让所有这些代码污染您的模块,您还可以创建一个侦听器类,并在引导方法中仅附加侦听器:
<?php
namespace Application\Listener;
use Application\Service\UserService;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Mvc\MvcEvent;
use Zend\EventManager\SharedEventManagerInterface;
use Zend\EventManager\SharedListenerAggregateInterface;
use Zend\Authentication\AuthenticationServiceInterface;
class IsAllowedListener implements SharedListenerAggregateInterface
{
/**
* @var AuthenticationServiceInterface
*/
protected $authService;
/**
* @var UserService
*/
protected $userService;
/**
* @var \Zend\Stdlib\CallbackHandler[]
*/
protected $sharedListeners = array();
/**
* @param SharedEventManagerInterface $events
*/
public function attachShared(SharedEventManagerInterface $events)
{
$this->sharedListeners[] = $events->attach(AbstractActionController::class, MvcEvent::EVENT_DISPATCH, array($this, 'isAllowed'), 1000);
}
public function __construct(AuthenticationServiceInterface $authService, UserService $userService ){
$this->authService = $authService;
$this->userService = $userService;
}
/**
* @param MvcEvent $event
*/
protected function isAllowed(MvcEvent $event)
{
$authService = $this->getAuthService();
$identity = $authService->getIdentity();
$userService = $this->getUserService();
if($userService->isGrantedCustom()){
// User is granted we can return
return;
}
// Return not allowed response
}
/**
* @return AuthenticationServiceInterface
*/
public function getAuthService()
{
return $this->authService;
}
/**
* @param AuthenticationServiceInterface $authService
*/
public function setAuthService(AuthenticationServiceInterface $authService)
{
$this->authService = $authService;
}
/**
* @return UserService
*/
public function getUserService()
{
return $this->userService;
}
/**
* @param UserService $userService
*/
public function setUserService(AuthenticationServiceInterface $userService)
{
$this->userService = $userService;
}
}
然后在引导中:
public function onBootstrap(EventInterface $event)
{
$application = $event->getTarget();
$serviceManager = $application->getServiceManager();
$eventManager = $application->getEventManager();
$sharedEventManager = $eventManager->getSharedManager();
$isAllowedListener = $serviceManager->get('Application\Listener\IsAllowedListener')
$sharedEventManager->attachAggregate($isAllowedListener);
}
您还可以创建一个特定的类,而不是使用AbstractActionController::class
,这样您将只侦听该类的实例
因此,例如
AbstractIsAllowedActionController::class
或类似的东西。如果您不想用所有这些代码污染您的模块,您还可以创建一个侦听器类,并在引导方法中只附加侦听器:
<?php
namespace Application\Listener;
use Application\Service\UserService;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Mvc\MvcEvent;
use Zend\EventManager\SharedEventManagerInterface;
use Zend\EventManager\SharedListenerAggregateInterface;
use Zend\Authentication\AuthenticationServiceInterface;
class IsAllowedListener implements SharedListenerAggregateInterface
{
/**
* @var AuthenticationServiceInterface
*/
protected $authService;
/**
* @var UserService
*/
protected $userService;
/**
* @var \Zend\Stdlib\CallbackHandler[]
*/
protected $sharedListeners = array();
/**
* @param SharedEventManagerInterface $events
*/
public function attachShared(SharedEventManagerInterface $events)
{
$this->sharedListeners[] = $events->attach(AbstractActionController::class, MvcEvent::EVENT_DISPATCH, array($this, 'isAllowed'), 1000);
}
public function __construct(AuthenticationServiceInterface $authService, UserService $userService ){
$this->authService = $authService;
$this->userService = $userService;
}
/**
* @param MvcEvent $event
*/
protected function isAllowed(MvcEvent $event)
{
$authService = $this->getAuthService();
$identity = $authService->getIdentity();
$userService = $this->getUserService();
if($userService->isGrantedCustom()){
// User is granted we can return
return;
}
// Return not allowed response
}
/**
* @return AuthenticationServiceInterface
*/
public function getAuthService()
{
return $this->authService;
}
/**
* @param AuthenticationServiceInterface $authService
*/
public function setAuthService(AuthenticationServiceInterface $authService)
{
$this->authService = $authService;
}
/**
* @return UserService
*/
public function getUserService()
{
return $this->userService;
}
/**
* @param UserService $userService
*/
public function setUserService(AuthenticationServiceInterface $userService)
{
$this->userService = $userService;
}
}
然后在引导中:
public function onBootstrap(EventInterface $event)
{
$application = $event->getTarget();
$serviceManager = $application->getServiceManager();
$eventManager = $application->getEventManager();
$sharedEventManager = $eventManager->getSharedManager();
$isAllowedListener = $serviceManager->get('Application\Listener\IsAllowedListener')
$sharedEventManager->attachAggregate($isAllowedListener);
}
您还可以创建一个特定的类,而不是使用AbstractActionController::class
,这样您将只侦听该类的实例
例如,
AbstractIsAllowedActionController::class
或类似的东西。从哪里可以获得ZF_INV_HOM参数?您应该将其连接到事件管理器。每个控制器都有一个被授予访问权限的权限名称,其在操作控制器本身中定义,并且与数据库中我的用户(自定义RBAC)的权限相匹配。您从何处获取ZF_INV_HOM参数?您应该将其连接到事件管理器。每个控制器都有一个权限名称,用于授予对其的访问权限,其在操作控制器本身中定义,并且与数据库中我的用户(自定义RBAC)的权限相匹配。我对高优先级有疑问,您确定1000高于-1000吗?我无法明确说明此链接和Listeners上的优先级不应在isAllowed(MvcEvent$event)中,如果($event->getTarget()instanceof ResourceInterface){/*…*/}
@venca如果您是正确的,您需要检查。我已经添加了它。@Hooli直截了当地说:attach()的第三个参数是优先级值。这个数字越高,监听器执行的越早;它越低,执行得越晚。该值默认为1,值将按给定优先级内注册的顺序触发。我对高优先级有疑问,您确定1000高于-1000吗?我无法明确说明此链接和Listeners上的优先级不应在isAllowed(MvcEvent$event)中,如果($event->getTarget()instanceof ResourceInterface){/*…*/}
@venca如果您是正确的,您需要检查。我已经添加了它。@Hooli直截了当地说:attach()的第三个参数是优先级值。这个数字越高,监听器执行的越早;它越低,执行得越晚。该值默认为1,值将按给定优先级内注册的顺序触发。+1这是最好的方法;您封装了侦听器逻辑,并使用服务工厂处理其依赖项。您说得对,根据您的评论,您涵盖了我的所有询问。谢谢:)@AlexP也谢谢+1这是最好的方法;您封装了侦听器逻辑,并使用服务工厂处理其依赖项。您说得对,根据您的评论,您涵盖了我的所有询问。谢谢:)@AlexP也谢谢