thinkphp5 第38課:正確使用表單令牌

使用表單令牌,一方面可以防止重複提交表單,二方面可以防止黑客CSRF。

表單令牌是在服務器端隨機生成的一串字符,保存在session中,並傳遞給前端頁面,當前端數據提交時,一併攜帶令牌提交給服務器,服務器驗證提交的令牌與session中的令牌是否一致,並同時銷燬session中的令牌。所以每次請求時的令牌只能使用一次,下次如果使用同樣的令牌就會失效,這即阻止了重複提交,也防止了黑客CSRF。

生成表單令牌經常使用以下兩種方式:

在前端頁面中 嵌入 <input type="hidden" name="__token__" value="{$Request.token}" />

<form id="form1" action="{:url('do_submit')}" method="post">
    <div>
        <label>學號</label>
        <input type="hidden" name="__token__" value="{$Request.token}">
        <input type="text" name="no">
    </div>
    <div>
        <label>姓名</label>
        <input type="text" name="name">
    </div>
    <div>
        <input type="submit" value="提交">
    </div>
</form>

在前端頁面嵌入{:token()}

<form id="form1" action="{:url('do_submit')}" method="post">
    <div>
        <lable>學號</lable>
        {:token()}
        <input type="text" name="no">
    </div>
    <div>
        <lable>姓名</lable>
        <input type="text" name="name">
    </div>
    <div>
        <input type="submit" value="提交">
    </div>
</form>

這兩種方式結果是一樣。{:token()} 最終也生成一個隱藏域,查看網頁源代碼:

<input type="hidden" name="__token__" value="a205d6c87ab5d2c0997a4586322405af" />

服務器端要做兩件事:

1、編寫驗證器和驗證規則

2、使用驗證器驗證提交的數據

以下是驗證器的參考代碼:

<?php
namespace app\index\validate;
use think\Validate;

/**
 * 驗證器
 */
class User extends Validate
{
    protected $rule = [
        "no" => 'require|token',
        "name" => 'require'
    ];

}
"no" => 'require|token' 
no 是表單中的文本框name="no"傳遞來的值,require表示必填,
token是驗證傳過來的表單令牌是否和服務器上的令牌一致,其實與 no 並無半毛錢關係

其中,這樣寫法很讓人產生誤解,不妨改成以下代碼更好理解

<?php
namespace app\index\validate;
use think\Validate;

/**
 * 驗證器
 */
class User extends Validate
{
    protected $rule = [
        "no" => 'require',
        "name" => 'require',
        "__token__" =>'require|token',
    ];

}

下面是調用驗證器的參考代碼

<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    public function index()
    {
        return $this->fetch();
    }

    public function do_submit()
    {
         $data = input('post.');
        $result = $this->validate($data,'User');
        dump($result);
    }
}

停牌驗證成功,輸出true

令牌驗證失敗:輸出"令牌數據無效"

如果在此頁面上刷新 ,就會出現以下結果:

表單令牌同樣適用於異步提交表單

前端參考代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="__STATIC__/jquery.min.js"></script>
</head>
<body>
<form id="form1" action="{:url('do_submit')}" method="post">
    <div>
        <lable>學號</lable>
        {:token()}
        <input type="text" name="no">
    </div>
    <div>
        <lable>姓名</lable>
        <input type="text" name="name">
    </div>
    <div>
        <input type="submit" value="提交">
    </div>
</form>
<script>
    $(function () {
        $('#form1').on('submit',function () {
            var data = $(this).serialize()
            var url = $(this).attr('action')
            $.ajax({
                type:'post',
                url:url,
                data:data,
                success:function (ret) {
                    console.log(ret)
                }
            })
            return false //阻止表單同步提交
        })
    })
</script>
</body>
</html>

服務器端參考代碼:

<?php

namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    public function index()
    {
        return $this->fetch();
    }

    public function do_submit()
    {
        $data = input('post.');
        $result = $this->validate($data, 'User');
        if (true === $result) {
            return ['code' => 200, 'message' => '驗證成功'];
        } else {
            return ['code' => 401, 'message' => $result];
        }
    }
}

如果學號和姓名空項提交,結果如下:

如果姓名爲空提交,結果如下:

學號和姓名都輸入,結果如下:

 

如果再次重複提交,結果如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章