zend form ,我们常用它的表单检查和大规模创建表单操作,但是好像它并没有给出防止表单重复提交的方案,除了在页面上做脚本(js)锁定操作的方案外,我们还要在服务器端做更加安全的检查。
下面介绍一下我的方案:
利用session(或缓存)和form token来防止表单的重复提交。核心是一个表单基类和一个validator。
表单基类:
<?php
/**
* 表单基类,负责添加表单令牌。
* @author Ju <hipop@126.com>
*
*/
class Application_Form_Base extends Zend_Form
{
const TOKEN_ID = "__form_token__";
public function init()
{
$tokenElement = $this->createElement('hidden',self::TOKEN_ID);
$tokenElement->addValidator("Formtoken")
->setRequired(TRUE)
->setValue($this->getTokenVal())
->addPrefixPath("Application_Validate",APPLICATION_PATH."/plugins/validators/","VALIDATE");
$this->addElement($tokenElement);
}
private function getTokenVal(){
return md5(time());
}
public function getToken(){
return $this->getElement(self::TOKEN_ID)->getValue();
}
/**
* 返回令牌错误,包装msg方法,方便使用
* @return string
*/
public function getTokenError(){
return $this->getMessages(self::TOKEN_ID);
}
}
插件:
<?php
/**
* 维护表单提交的token队列
* @author Ju <hipop@126.com>
*
*/
class Application_Validate_Formtoken extends Zend_Validate_Abstract
{
const NOT_MATCH = 'doNotResubmit';
protected $_messageTemplates = array(
self::NOT_MATCH => '请不要重复提交表单!'
);
private $_listLength = 10;
private $_tokenList; //固定长度数组
private $_sessionTokenLst;
public function __construct(){
Zend_Session::start();
$this->_sessionTokenLst = new Zend_Session_Namespace("FORMTOKENLST");
$this->_getTokenList();
}
/**
* 从session中获取token队列
* Enter description here ...
*/
private function _getTokenList(){
$this->_tokenList = $this->_sessionTokenLst->TOKENLIST;
$this->_tokenList = is_array($this->_tokenList)?$this->_tokenList:array();
}
/**
* 将队列放入session
* Enter description here ...
* @param array $tokenlist
*/
private function _setTokenList($tokenlist=NULL){
$tokenlist = is_array($tokenlist)?$tokenlist:array();
if(count($tokenlist)>$this->_listLength){
array_shift($tokenlist);
return $this->_setTokenList($tokenlist);
}
$this->_sessionTokenLst->TOKENLIST = $tokenlist;
}
/**
* 检查token
* Enter description here ...
* @param string $token
* @return boolean
*/
public function check($token){
if (in_array($token,$this->_tokenList)){
$this->_error(self::NOT_MATCH);
return FALSE;
}else {
array_push($this->_tokenList ,$token);
$this->_setTokenList($this->_tokenList);
return TRUE;
}
}
/* (non-PHPdoc)
* @see Zend_Validate_Interface::isValid()
*/
public function isValid($value) {
// TODO Auto-generated method stub
return $this->check($value);
}
}
使用的时候,表单要继承基类,并调用父类 的初始化方法。然后再isValid就ok了。