zf2 acl转发到登录表单

时间:2017-09-09 10:02:30

标签: php redirect zend-framework2 acl forward

我想将未经身份验证的用户(访客)转发到登录表单。

使用重定向时,我需要将访客用户从他们正在寻找的页面重定向到登录页面;然后再将它们重新定向。

示例:

(访客访问)mySite / controller / action?var1 = xxx& var2 = yyy

(AclService重定向)mySite / login

(AuthService重定向)mySite / controller / action?var1 = xxx& var2 = yyy

所有这一切只能使用会话变量(我猜)。

我的想法是将用户转发到mySite / login。验证成功后,我唯一需要做的就是重定向到当前URL。 这种行为的好处是,如果用户单击浏览器的后退按钮,页面保持不变(mySite / controller / action?var1 = xxx& var2 = yyy)。

这是我的代码:

在module.php中

public function onBootstrap(MvcEvent $e){

$app = $e->getApplication();
$sm  = $app->getServiceManager();
$acl = $sm->get('AclService');
$e -> getApplication()-> getEventManager()->getSharedManager()->attach('Zend\Mvc\Controller\AbstractActionController',MvcEvent::EVENT_DISPATCH,array($acl, 'checkAcl'),-10);

}

在我的AclService中,checkAcl函数

[...]

if (!$this->acl ->isAllowed($role, $controller, $action)){

   //when my AuthService->hasIdentity() returns false, the the role is guest
   if($role=='guest'){ 
      $controllerClass = get_class($e->getTarget());
         //this prevents nested forwards
         if($controllerClass!='Auth\Controller\Auth2Controller'){
         $e->setResult($e->getTarget()->forward()->dispatch('Auth\Controller\Auth2',array('action'=>'index')));
          }

    }
    else{...}
}

然后在我的AuthService中,我使用此函数(在mysite / login中调用)来重定向经过身份验证的用户

//if the login page has ben forwarded
if($e->getRouteMatch()->getParam('controller')!='Auth\Controller\Auth2'
{
$url=$this->Config['webhost'].$e->getRequest()->getRequestUri();
return $e->getTarget()->redirect()->toUrl($url);
}
//if the request comes directly from a login/register attempt
else{return $e->getTarget()->redirect()->toRoute('user_home');}

你怎么看?这有道理吗?

你知道更好的方法吗?

1 个答案:

答案 0 :(得分:0)

回答我自己的问题:

不,这没有任何意义。那是因为

  

转发是一种在另一个控制器上发送控制器的模式   控制器已经发送   (Forward to another controller/action from module.php

因此,acl检查部分无用,因为放入受保护页面的任何内容都将在转发之前执行。 这意味着如果我有一个仅为经过身份验证的用户设计的用户页面(控制器),我会调用类似的函数:

$name=$this->getUserName($userId);

我将收到一条错误消息,因为在验证过程中应该设置var $ userId。等等...

无论如何,我找到了一个更好的解决方案,行为完全不同,但结果相同(显然没有上述问题)。

然而,由于我原来的问题非常具体,为了清楚起见,我想更好地解释前缀目标

我有一个在调度之前运行的acl脚本,如果用户未经过身份验证(role = guest),则会将用户重定向到登录表单。

当用户通过登录表单发送凭据并进行身份验证时,我希望根据他们到达表单的方式执行动态重定向

动态重定向应遵循以下条件:

  1. 当用户通过www.site.com/login访问表单时,经过身份验证后,应将其重定向到www.site.com/user_home

  2. 当用户通过www.site.com/controller/action/param?var=xxx&var2=yyy [...]访问表单时,经过身份验证后,应将其重定向到相同的网址,而不会丢失任何内容参数。

  3. 用户通过身份验证后,无法访问www.site.com/login页面(因为没用)。这意味着寻找此页面的用户应该被重定向到其他地方。

  4. 但我也是从“重定向不好”的假设开始的

    1. 如果没有直接联系到,浏览器历史记录中不应有任何www.site.com/login的痕迹。 这是因为浏览器的后退按钮可能会搞砸第2点所说的任何内容(冲突)。例如:

      • 我到达网页site.com/user/post/2016?tag=foo但我没有登录或会话已过期
      • 然后我被重定向到www.site.com/login,在那里我填写认证表格并提交
      • 经过身份验证后,我会再次被重定向到site.com/user/post/2016?tag=foo
      • 但是,如果我通过浏览器的按钮返回,那么我的请求的来源是www.site.com/login,如第1点和第3点所定义, 我将被重定向到www.site.com/user_home,无法返回site.com/user/post/2016?tag=foo(如果不是通过历史记录)。 (这就是我尝试使用前插件的原因)
    2. 我发现实现这一目标的解决方案是使用重定向插件以及与MVC的Routemach一起使用的自助转发过程。

      我按照这个samsonasik的教程开始编码:https://samsonasik.wordpress.com/2013/05/29/zend-framework-2-working-with-authenticationservice-and-db-session-save-handler/

      与samsonasik代码的主要区别在于我添加了一个名为authService的服务,它分别处理与身份验证相关的功能。 这只是为了使代码更具可读性/可重用性,因为我需要一些自定义逻辑(与我的应用程序设计相关)才能在身份验证上执行。

      这是我结构的主要部分:

      Application
      Auth
          ->service
                   ->authService
                   ->aclService
      Guest
           ->Controller
                       ->guestHomeController
                                            ->loginAction
                                            ->singUpAction
                                            ->...
                       ->...
      User
          ->Controller
                      ->userHomeController
                      ->...
      

      guest虚拟机模块中的任何内容都应该可供public(role = guest)访问,但不能访问已经过身份验证的用户(根据上面的第3点)。

      最后,代码:

      路径:Auth / Module.php

      namespace Auth;
      
      use Zend\Mvc\MvcEvent;
      
      class Module
      {
      
        public function onBootstrap(MvcEvent $e)
        {
          $app = $e->getApplication();
          $sm  = $app->getServiceManager();
      
          $acl = $sm->get('aclService');
      
          $acl->initAcl();
          $e -> getApplication() -> getEventManager() -> attach(MvcEvent::EVENT_ROUTE, array($acl, 'checkAcl'));
        }
      
        [...]
      }
      

      路径:Auth / src / Auth / Service / AclService.php

      namespace Auth\Service;
      
      use Zend\Permissions\Acl\Acl;
      use Zend\Permissions\Acl\Role\GenericRole as Role;
      use Zend\Permissions\Acl\Resource\GenericResource as Resource;
      use Zend\Mvc\MvcEvent;
      
      
      class AclService  {
      
      protected $authService;
      protected $config;
      protected $acl;
      protected $role;
      
      
          public function __construct($authService,$config)
          {
              if(!$this->acl){$this->acl=new Acl();}
              $this->authService = $authService;
              $this->config = $config;
          }
      
      
          public function initAcl()
          {
             $this->acl->addRole(new Role('guest'));
             [...]
      
             $this->acl->addResource(new Resource('Guest\Controller\GuestHome'));
             [...]
      
             $this->acl->allow('role', 'controller','action');
             [...]
          }
      
      
         public function checkAcl(MvcEvent $e)
          {
            $controller=$e -> getRouteMatch()->getParam('controller');
            $action=$e -> getRouteMatch()->getParam('action');
            $role=$this->getRole();
      
            if (!$this->acl ->isAllowed($role, $controller, $action)){
      
              //if user isn't authenticated...
              if($role=='guest'){
                //set current route controller and action (this doesn't modify the url, it's non a redirect)
                $e -> getRouteMatch()->setParam('controller', 'Guest\Controller\GuestHome');
                $e -> getRouteMatch()->setParam('action', 'login');
              }//if
      
              //if user is authenticated but has not the permission...
              else{
                $response = $e -> getResponse();
                $response -> getHeaders() -> addHeaderLine('Location', $e -> getRequest() -> getBaseUrl() . '/404');
                $response -> setStatusCode(404);
              }//else
            }//if
          }
      
         public function getRole()
         {
         if($this->authService->hasIdentity()){
         [...]
         }
         else{$role='guest';}
         return $role;
         }
      
         [...]
      
      }
      

      路径:Guest / src / Guest / Controller / GuestHomeController.php

      namespace Guest\Controller;
      
      use Zend\Mvc\Controller\AbstractActionController;
      use Zend\View\Model\ViewModel;
      use Zend\Form\Factory;
      
      class GuestHomeController extends AbstractActionController
      {
      
          protected $authService;
          protected $config;
          protected $routeName;
      
          public function __construct($authService, $config, $routeMatch)
          {
              $this->authService = $authService;
              $this->config = $config;
              $this->routeName = $routeMatch->getMatchedRouteName();
          }
      
      
          public function loginAction()
          {
      
            $forwardMessage=null;
      
            //When users DOESN'T reach the form via www.site.com/login (they get forwarded), set a message to tell them what's going on
            if($this->routeName!='login'){
              $forwardMessage='You must be logged in to see this page!!!';
            }
      
            //This could probably be moved elsewhere and be triggered (when module is "Guest") on dispatch event with maximum priority (predispatch)
            if ($this->authService->hasIdentity()) {
              $this->dynamicRedirect();
            }
      
            $factory = new Factory();
            $form = $factory->createForm($this->config['LoginForm']);
      
            $request=$this->getRequest();
      
            if ($request->isPost()) {
      
              $form->setData($request->getPost());
      
              if ($form->isValid()) {
      
                $dataform = $form->getData();
                $result=$this->authService->authenticate($dataform);
      
                //result=status code
                if($result===1){$this->dynamicRedirect();}
                else{...}
              }//$form->isValid()
            }//$request->isPost()
      
      
            $viewModel = new ViewModel();
            $viewModel->setVariable('form', $form);
            $viewModel->setVariable('error', $forwardMessage);
      
            return $viewModel;
          }
      
      
          public function dynamicRedirect()
          {
            //if the form has been forwarded
            if($this->routeName!='login'){
              $url=$this->config['webhost'].$this->getRequest()->getRequestUri();
              return $this->redirect()->toUrl($url);
            }
            else{return $this->redirect()->toRoute('userHome');}//else
          }
      
          [...]
      }
      

      如果我发现其他一些问题,我会更新这篇文章,但现在它就像一个魅力。

      P.S。:希望我的英语结果可读。 : - )