2

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

  1. 在 POST 中调用,而不是 GET 或其他方法
  2. 并拥有我在视图中的表单中设置的 pageInstanceID
    • 防止 xss
    • 防止重复提交表单
      • 从提交按钮双击
      • 提交后按下后退按钮
      • 来自正在保存或添加书签的 url

目前,我使用 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

4 回答 4

2

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

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

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

以这种方式检查 CSRF 令牌:

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

您甚至可以通过以下方式检查使用的 HTTP 方法is()

$this->request->is('post');

过滤器的问题(对于那个用例)是它们非常通用。因此,如果您不想将所有操作都编写为可过滤的代码(这可能会很痛苦且过大),您必须找到一种方法来定义哪个方法会阻止什么并过滤Dispatcher::_call.

于 2012-03-21T12:30:31.687 回答
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
});
于 2012-03-21T02:21:15.163 回答
0

我在最近的一个项目中实现了类似的东西,方法是子类\lithium\action\Controller化为app\controllers\ApplicationController(abstract) 并将过滤器应用于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()inside of_init()在你的方法上运行过滤器。$method您可以选择更改_filter(__METHOD__, . . .)为,而不是检查每个过滤器_filter($method, . . .),但我们选择保留更通用的过滤器。

于 2013-04-02T20:53:25.043 回答
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。

于 2012-03-21T18:43:32.613 回答