php语言 百分网手机站

如何真正解决表单重复提交问题php代码(2)

时间:2020-11-12 15:07:40 php语言 我要投稿

如何真正解决表单重复提交问题php代码

  正如令牌本身代表着权限,操作权,身份标志等等,所以我能不能为我的表单加上这么 一个身份标志,在客户端每次请求这个表单的时候同时生成一个令牌其挂钩,在提交时再进行判断,正确则接收并处理表单。实现本质就是如此,而反映到具体实现上,就需要用到一种叫 session的东西。关于session的解析,参见wiki

  简单的理解就是session也是一种令 牌的概念,所以你可能会很惊奇,“什么我已经使用了令牌?!”,是的,但是我们要实现的不仅仅是session而是在其基础上附加一些数据来实现我们想要的表单令牌。So let's do it!

  session在php中也是被存放在$_SESSION这个超级全局变量里面的,启用起请使用session_start(),关于其他服务端脚本原理一样,只是可能调用方法名不一致而已。加入 token后的代码如下:

 代码如下  
   <?php
    //开启session
    session_start();
    if ( isset( $_POST['submit'] ) && valid_token() ) {
    //表单
    
    提交处理
    }
    
    /**
    * 生成令牌
    * @return string MD5加密后的时间戳
    */
    function create_token() {
    //当前时间戳
    
    $timestamp = time();
    $_SESSION['token'] = $timestamp;
    return md5( $timestamp );
    }
    /**
    * 是否有效令牌
    * @return bool
    
    
    */
    function valid_token() {
    if ( isset( $_SESSION['token'] ) && isset( $_POST['token'] ) && $_POST['token'] == md5(
    
    $_SESSION['token'] ) )
    {
    //若正确将本次令牌销毁掉
    $_SESSION['token'] = '';
    return true;
    }
    return false;
    }
    ?>
    <?php if ( isset( $state ) && $state ) { //数据插入成功 ?>
    <p>插入成功 <a href="form.php">返回
    
    </a></p>
    <?php } else { //失败或者没有插入动作 ?>
    <form method="post">
    <p>
    <label for="test">随便
    
    输入点什么</label>
    <input type="text" name="data" id="test" />
    </p>
    <p>
    <input type="submit" value="提
    
    交" name="submit" />
    </p>
    </ul>
    <!-- Token -->
    <input type="hidden" value="<?php echo create_token(); //生成
    
    令牌 ?>" name="token" />
    <!-- Token -->
    </form>
    <?php } ?>

  部分代码paperen这里省略,因为并不是重点,其实加的 东西只有3处:

  第一,在表单结束前加入了一个input元素,记得type为hidden(隐藏域)

  第二,增加了两个函数,create_token与valid_token,前者用来生成令牌 的后者用来验证令牌的

  第三,在if中多加一个条件,valid_token

  那就大功告成了,很简单,而且所有的东西都聚集在新加的两个函数中。paperen这里使用的令牌很 简单就是时间戳,将请求表单时的时间戳存储到$_SESSION['token']中,那么验证令牌就明白了,就是检查客户端提交过来的$_POST['token']是否与md5后的$_SESSION['token']一致 就行了,当然还要加上存在$_POST['token']与$_SESSION['token']这两个变量才行。

  你可以将这个简单的令牌模式封装得更加棒并扩展一下功能,例如加上表单提交超时验证 也是个不错的动手机会。

  最后附上之前paperen扩展codeingeter的Form_validation类文件,主要是扩展上令牌与表单超时。压缩包中welcome.php是控制器文件,请放置到 applicationcontroller中(如不想增加这个控制器可以打开然后将token方法复制下来放到已有的其他控制器中);MY_Form_validation.php请放到applicationlibraries中。

  codeingeter的Form_validation类文件代码

 代码如下  

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Welcome extends CI_Controller {


 public function index()
 {
  $this->load->view('welcome_message');
 }

        public function token()
        {
            $this->load->helper( array('form') );
            $this->load->library('form_validation');
            if ( $this->input->post( 'submit' ) && $this->form_validation->valid_token() )
            {
                //nothing
                //valid_token已经包含令牌超时与令牌正确的判断,若要启用令牌超时,请将token_validtime设置为非0
                echo 'ok';
            }

            //生成表单令牌
            $token = $this->form_validation->create_token();

            //form example
            echo form_open();
            echo form_input('token', '');
            echo $token;
            echo form_submit('submit', 'submit');
            echo form_close();
        }
}

  form_validation_token

 代码如下  

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * @abstract 继承CI的Form_validation类在其基础上增加令牌
 */
class MY_Form_validation extends CI_Form_validation {

    /**
     * 令牌键值
     * @var string
     */
    var $key = 'token';
   
    /**
     * 表单令牌有效时间(秒)
     * @abstract 如果某些表单需要限制输入时间,则设置该值,为0则不限制
     * @var int 秒
     */
    var $token_validtime = 5;

    /**
     * 调试模式
     * @var bool
     */
    var $debug = false;

    /**
     * CI对象
     * @var <type>
     */
    private $_CI;

    public function __construct()
    {
        parent::__construct();
        $this->_CI =& get_instance();
        //如果配置没有填写encryption_key
        $encryption_key = config_item('encryption_key');
        if ( empty( $encryption_key ) ) $this->_CI->config->set_item( 'encryption_key', $this->key );
        //如果没有装载session
        if ( !isset( $this->_CI->session ) ) $this->_CI->load->library('session');
    }

    /**
     * 设置令牌有效时间
     * @param int $second 秒数
     */
    public function set_token_validtime( $second )
    {
        $this->token_validtime = intval( $second );
    }

    /**
     * 获取表单令牌有效时间
     * @return int 秒数
     */
    public function get_token_validtime()
    {
        return $this->token_validtime;
    }

    /**
     * 验证表单令牌是否合法
     * @return bool
     */
    public function valid_token()
    {
        if ( $this->debug ) return true;
        //获取session中的hash
        $source_hash = $this->_CI->session->userdata( $this->key );
        if ( empty( $source_hash ) ) return false;
       
        //判断是否超时
        if ( $this->is_token_exprie() ) return false;

        //提交的hash
        $post_formhash = $this->_CI->input->post( $this->key );
        if ( empty( $post_formhash ) ) return false;

        if ( md5( $source_hash ) == $post_formhash )
        {
            $this->_CI->session->unset_userdata( $this->key );
            return true;
        }
        return false;
    }

    /**
     * 生成表单令牌(连同input元素)
     * @return string
     */
    public function create_token( $output = false )
    {
        $code = time() . '|' .  $this->get_random( 5 );
        $this->_CI->session->set_userdata( $this->key , $code);
        $result = function_exists('form_hidden') ? form_hidden( $this->key, md5( $code ) ) : '<input type="hidden" name="token" value="'. md5( $code ) .'" />';
        if ( $output )
        {
            echo $result;
        }
        else
        {
            return $result;
        }
    }
   
    /**
     * 获取随机数(自己可以扩展)
     * @param int $number 上限
     * @return string
     */
    public function get_random( $number )
    {
        return rand( 0, $number );
    }
   
    /**
     * 判断表单令牌是否过期
     * @return bool
     */
    public function is_token_exprie()
    {
        if ( empty( $this->token_validtime ) ) return false;
        $token = $this->_CI->session->userdata( $this->key );
        if ( empty( $token ) ) return false;
        $create_time = array_shift( explode('|', $token) );
        return ( time() - $create_time > $this->token_validtime );
    }
}


【如何真正解决表单重复提交问题php代码】相关文章:

1.php防止表单重复提交的方法

2.PHP代码如何规范

3.php如何解决中文乱码问题

4.如何正确发布PHP代码

5.如何阅读php源代码

6.如何真正解决物业管理问题?

7.PHP 表单验证方法

8.解决PHP中文乱码问题

9.如何在HTML中嵌入PHP代码