首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

ThinkPHP 表單令牌錯誤原因分析與解決方案 (字段快取遺失、配置關閉、模板標籤)

     編輯:PHP 相關知識

ThinkPHP 表單令牌錯誤:根因分析與三大解決方案

ThinkPHP 內建表單令牌驗證 (防 CSRF),開啟 TOKEN_ON 後,若提交報錯 "表單令牌錯誤",常見原因有三類:**字段快取檔案遺失/損壞、令牌驗證被錯誤關閉導致資料空值、模板缺少令牌隱藏域**。本文逐一剖析並給出修復步驟。

一、錯誤現象

// 控制器 create() 方法中
$model = M('User');
if ($model->create()) { // 內部自動驗證令牌
    $model->add();
} else {
    $this->error($model->getError()); // 輸出: "表單令牌錯誤"
}

二、原因 1:字段快取遺失/損壞 (最隱蔽、最常見)

原理

ThinkPHP 為避免每次查詢 SHOW COLUMNS,會將資料表結構快取至 Runtime/Data/_fields/ 目錄下 (每表一個 PHP 回傳陣列檔)。模型 create() 依賴此快取判斷哪些欄位合法、哪些是主鍵/自增。若快取檔案遺失、被誤刪、或內容被清空 (僅剩 '_autoinc' => true),模型會認為**所有傳入欄位皆非法**,導致 create() 回傳 false,錯誤訊息卻誤導為 "表單令牌錯誤"。

快取檔案正常範例

// Runtime/Data/_fields/user.php
<?php return array(
    0 => 'id',
    1 => 'username',
    2 => 'password',
    3 => 'email',
    '_autoinc' => true,
    '_pk' => 'id',
); ?>

損壞範例 (僅剩 autoinc)

<?php return array(
    '_autoinc' => true,
); ?>

修復步驟

  1. 進入 Runtime/Data/_fields/ 目錄。
  2. 刪除對應模型的快取檔 (如 user.php)。
  3. 重新整理頁面提交表單,框架會自動重新查詢資料庫結構並重建快取。
  4. 若仍失效,檢查 Runtime/ 目錄寫入權限 (Linux: chmod -R 775 Runtime/、擁有者 www-data)。

三、原因 2:錯誤關閉令牌驗證導致資料空值

部分開發者為「快速解決」在配置中設 'TOKEN_ON' => false 或模板加 {__NOTOKEN__}。雖不再報錯,但 create() 因無字段快取 (見原因 1) 仍**無法自動過濾合法欄位**,導致 $model->data 為空,**寫入資料庫的全是 NULL/預設值**。

正確做法:修復字段快取 (原因 1) 後,**保持令牌驗證開啟** (預設即開啟),安全與功能兼得。

四、原因 3:模板確實缺少令牌隱藏域

若字段快取正常,但模板表單**未使用 ThinkPHP 表單標籤** 或 **手寫 HTML 未包含令牌隱藏域**,則真實觸發令牌驗證失敗。

正確模板寫法

<form action="__URL__/insert" method="post">
    <input type="text" name="username" />
    <input type="password" name="password" />
    <!-- ThinkPHP 表單標籤自動生成令牌隱藏域 -->
    <!-- 或手寫: -->
    <input type="hidden" name="__TOKEN__" value="{:session('__TOKEN__')}" />
    <input type="submit" value="提交" />
</form>

或使用內建標籤 (自動注入令牌):

<form action="__URL__/insert" method="post">
    <input type="text" name="username" />
    <input type="password" name="password" />
    {:token()} <!-- 模板標籤輸出隱藏域 -->
    <input type="submit" value="提交" />
</form>

五、排查清單 (由易到難)

  1. 清除快取:刪除 Runtime/Cache/, Runtime/Data/_fields/ 相關檔,重試。
  2. 檢查權限:Runtime/ 目錄 Web 使用者可讀寫。
  3. 確認模板:表單包含 {:token()} 或 <input name="__TOKEN__">。
  4. 確認配置:'TOKEN_ON' => true (預設),'TOKEN_NAME' => '__TOKEN__' (預設)。
  5. 檢查 Session:Session 正常啟動、可寫、未過期 (令牌存於 Session)。
  6. 多標籤/並發提交:用戶開多個編輯頁,Session 令牌被覆蓋 → 先前頁提交失敗。解決:TOKEN_TYPE 設為 session (預設) 或改用 cookie,或前端提交前刷新令牌 (AJAX 取新令牌)。

六、總結

90% 以上的 "表單令牌錯誤" 實為字段快取遺失導致的誤報。遇到此錯誤,**首要動作是刪除 Runtime/Data/_fields/ 下對應模型快取檔**,而非關閉令牌驗證。保持令牌開啟、快取完整、模板正確,即可同時擁有 CSRF 防護與正常表單提交。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved