PHP表单TOKEN防止重复提交

PHP表单TOKEN防止重复提交

一 简介

1. 什么是表单token?
Token(人家就叫这名,记住就行)是服务端生成的一串字符串,以作客户端进行请求的一个令牌和依据。
2. 表单token用来干什么?
第一:防止提交表单是为外部提交。也叫(跨站点请求伪造)。
第二:防止表单重复提交,造成数据错乱。
3. 表单token的实现原理是什么?
在用户进入任何一个表单页面的时候,服务器端就会生成一段针对该用户的唯一token码,同时该token码会保存到该用户session_id下session数据中。这个token码就在表单的隐藏域中。在用户提交该表单的时候会跟随其他数据一并提交。后台会进行数据验证,包括什么用户名验证(不为空,不能包含中文等),密码验证(不能全为数字,不能少于10位等),还有其他乱其八糟的验证。其中就包括token验证(与该用户session_id下储存的token码进行比对),如果token为空或者不等于当前session中存储的token码,就说明该表单为外部提交表单或者是重复提交表单,将不在进行数据库操作。如果token和其他数据都验证通过了,(先把session中的token码清空)进行数据库操作,提交数据。

注意:不论token码验证失败还是成功,都必须进行清空。如果验证失败了没有清空session中的token,如果你的验证规则不好(token不唯一),可能多试几次就会通过了。如果验证成功了,页面没有进行跳转,你也没清空session中的token,多点几次提交,这些数据也都会提交。

二实例

这里我那TP5框架里的表单验证来举例。
商品分类表单提交页面:
这里写图片描述
表单后台代码(只贴一部分):

<div class="ncap-form-default">        
            <div class="bot">            
                <input type="hidden" name="id" value="{$Info.id}">
                <input type="hidden" name="__token__" value="{$Request.token}" />
                <a href="JavaScript:void(0);" class="ncap-btn-big ncap-btn-green" onClick="ajax_submit_form('addEditCoordinatesForm','{:U('Coordinates/addEditCoordinates?is_ajax=1')}');">确认提交</a>
            </div>
        </div> 
     </form>

后台代码:

if((I('is_ajax')==1) && IS_POST){
          $data = I('post.');
          $validate = \think\Loader::validate('Goods');
          if (!$validate->batch()->check($data)) {//数据验证
                $error = $validate->getError();
                $error_msg = array_values($error);
                $return_arr = array(
                    'status' => -1,
                    'msg' => $error_msg[0],
                    'data' => $error,
                );
                $this->ajaxReturn($return_arr);
            }

这段代码是后台调取验证规则的,以下是我的验证规则:

<?php
namespace app\admin\validate;
use think\Validate;
class Goods extends Validate
{

    // 验证规则
    protected $rule = [
        ['Goods_name','require|unique:goods','商品名称必填|商品名称重复'],
        ['Goods_sn', 'unique:goods', '商品货号重复'], // 更多 内置规则 http://www.kancloud.cn/manual/thinkphp5/129356
        ['shop_price','regex:\d{1,10}(\.\d{1,2})?$','本店售价格式不对。'],
        ['market_price','regex:\d{1,10}(\.\d{1,2})?$','市场价格式不对。'],
        ['weight','regex:\d{1,10}(\.\d{1,2})?$','重量格式不对。'],
        ['exchange_integral','checkExchangeIntegral','积分抵扣金额不能超过商品总额'],
        ['__token__','token','正在拼了死命的加载中······'],
    ];

验证规则数组中的一个值是要验证的字段,第二个值时要调用的规则(可以自定义规则),第三个是提示信息。TP5的官方手册的写法让人困惑感觉好像是token验证是追加到了某个字段(如name字段)的后边,好像必须要依附于某个字段才能进行验证。
这里写图片描述
先不提官方文档。我这样写是标准格式,绝对没有问题的。

注意:这里需要注意一个细节,观察token源码,发现生成token是在Request类里:

/**
     * 生成请求令牌
     * @access public
     * @param string $name 令牌名称
     * @param mixed  $type 令牌生成方法
     * @return string
     */
    public function token($name = '__token__', $type = 'md5')
    {
        $type  = is_callable($type) ? $type : 'md5';
        $token = call_user_func($type, $_SERVER['REQUEST_TIME_FLOAT']);
        if ($this->isAjax()) {
            header($name . ': ' . $token);
        }
        Session::set($name, $token);
        return $token;
    }

然后验证token是在Validate类里:

/**
     * 验证表单令牌
     * @access protected
     * @param mixed     $value  字段值
     * @param mixed     $rule  验证规则
     * @param array     $data  数据
     * @return bool
     */
    protected function token($value, $rule, $data)
    {
        $rule = !empty($rule) ? $rule : '__token__';
        if (!isset($data[$rule]) || !Session::has($rule)) {
            // 令牌数据无效
            return false;
        }

        // 令牌验证
        if (isset($data[$rule]) && Session::get($rule) === $data[$rule]) {
            // 防止重复提交
            Session::delete($rule); // 验证完成销毁session
            return true;
        }
        // 开启TOKEN重置
        Session::delete($rule);
        return false;
    }

而每次验证完token,都会进行清空,所以所有的其他验证规则到要放到token验证之前,不然token验证完成了,而其他有的字段验证未通过,你的表单是需要重新填写的,否则会永远提交不上。放到最后边,即使其他字段验证失败,从新修改那个字段继续提交即可,不需要重新填写表单。哒哒······结束啦。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值