控制器中的过滤方法

时间:2012-03-21 01:56:20

标签: php aop lithium

我想在我的控制器中为我的添加,更新和删除操作创建一个过滤器,以自动检查它们是否

  1. 在POST中被调用,而不是GET或其他方法
  2. 并拥有我在视图中的表单中设置的pageInstanceID
    • 防范xss
    • 防止双重提交表单
      • 从提交按钮双击
      • 从后面的按钮按下
      • 来自保存或加入书签的网址
  3. 目前,我使用AppController扩展了\ lithium \ action \ Controller,并在其中定义了添加,更新和删除操作。 我的AppController中还有一个布尔函数,用于检查相应的pageInstanceID是否在会话中。

    以下是我的代码:

    public function isNotPostBack() {
    
        // pull in the session
        $pageInstanceIDs = Session::read('pageInstanceIDs');
        $pageInstanceID = uniqid('', true);
        $this->set(compact('pageInstanceID'));
        $pageInstanceIDs[] = $pageInstanceID;
        Session::write('pageInstanceIDs', $pageInstanceIDs);
    
        // checks if this is a save operation
        if ($this->request->data){
    
            $pageInstanceIDs = Session::read('pageInstanceIDs');
            $pageIDIndex = array_search($this->request->data['pageInstanceID'], $pageInstanceIDs);
    
            if ($pageIDIndex !== false) {
                // remove the key
                unset($pageInstanceIDs[$pageIDIndex]);
                Session::write('pageInstanceIDs', $pageInstanceIDs);
    
                return true;
    
            }
            else
                return false;
    
        } else {
            return true;
        }
    
    }
    
    public function add() {
        if (!$this->request->is('post') && exist($this->request->data())) {
            $msg = "Add can only be called with http:post.";
            throw new DispatchException($msg);
        }
    
    }
    

    然后在我的控制器中,我从AppController继承并执行如下操作:

    public function add() {
        parent::add();
        if (parent::isNotPostBack()){
            //do work
    
        }
        return $this->render(array('layout' => false));
    
    }
    

    这将确保表单使用POST并未双重提交(后退按钮或单击快乐用户)。这也有助于防止XSS。

    我知道有一个插件,但我希望将其作为过滤器实现,以便我的控制器方法更清晰。以这种方式实现,我的操作中唯一的代码是// do work部分和return语句。

4 个答案:

答案 0 :(得分:2)

你应该从lithium\action\Dispatcher::run()上的过滤器开始,这里有一些伪代码。如果没有看到parent::isNotPostBack()方法,就无法帮助太多,但这会让你走上正轨。

<?php
use lithium\action\Dispatcher;

Dispatcher::applyFilter('run', function($self, $params, $chain) {
    $request = $params['request'];

    // Request method is in $request->method
    // Post data is in $request->data

    if($not_your_conditions) {
        return new Response(); // set up your custom response
    }

    return $chain->next($self, $params, $chain); // to continue on the path of execution
});

答案 1 :(得分:2)

首先,使用集成的CSRF(XSRF)保护。

  

RequestToken类创建加密安全的令牌和密钥,可用于验证客户端请求的真实性。

     

- http://li3.me/docs/lithium/security/validation/RequestToken

以这种方式检查CSRF令牌:

if ($this->request->data && !RequestToken::check($this->request)) {
    /* do your stuff */
}

您甚至可以通过is()

检查使用的HTTP方法
$this->request->is('post');

过滤器(针对该用例)的问题在于它们非常通用。因此,如果您不想将所有操作都写为可过滤的代码(这可能会让人感到痛苦和过度杀伤),那么您必须找到一种方法来定义哪种方法阻止了什么并过滤了Dispatcher::_call

答案 2 :(得分:0)

对于CSRF保护,我使用类似于greut的建议。

我在extensions/action/Controller.php

中有这个
protected function _init() {
    parent::_init();

    if ($this->request->is('post') ||
        $this->request->is('put') ||
        $this->request->is('delete')) {
        //on add, update and delete, if the security token exists, we will verify the token
        if ('' != Session::read('security.token') && !RequestToken::check($this->request)) {
            RequestToken::get(array('regenerate' => true));
            throw new DispatchException('There was an error submitting the form.');
        }
    }
}

当然,这意味着您还必须将以下内容添加到文件顶部:

use \lithium\storage\Session;
use lithium\security\validation\RequestToken;
use lithium\action\DispatchException;

有了这个,我不必反复检查CSRF。

答案 3 :(得分:0)

我通过将\lithium\action\Controller子类化为app\controllers\ApplicationController(摘要)并将过滤器应用于invokeMethod(),在最近的项目中实现了类似的功能,因为这就是调度程序调用操作方法的方式。这是相关的块:

namespace app\controllers;

class ApplicationController extends \lithium\action\Controller {
    /**
     * Essential because you cannot invoke `parent::invokeMethod()` from within the closure passed to `_filter()`... But it makes me sad.
     *
     * @see \lithium\action\Controller::invokeMethod()
     *
     * @param string $method to be invoked with $arguments
     * @param array $arguments to pass to $method
     */
    public function _invokeMethod($method, array $arguments = array()) {
        return parent::invokeMethod($method, $arguments);
    }

    /**
     * Overridden to make action methods filterable with `applyFilter()`
     *
     * @see \lithium\action\Controller::invokeMethod()
     * @see \lithium\core\Object::applyFilter()
     *
     * @param string $method to be invoked with $arguments
     * @param array $arguments to pass to $method
     */
    public function invokeMethod($method, array $arguments = array()) {
        return $this->_filter(__METHOD__, compact('method', 'arguments'), function($self, $params){
            return $self->_invokeMethod($params['method'], $params['arguments']);
        });
    }
}

然后,您可以使用applyFilter()内的_init()在您的方法上运行过滤器。您可以选择将$method更改为_filter(__METHOD__, . . .),而不是检查每个过滤器中的_filter($method, . . .),但我们选择保留更通用的过滤器。