最近在拿 yii 2.0 開發ajax提交,在 post 請求接口時,提示數據無法驗證,於是查詢 yii 錯誤日志,發現錯誤為
exception ‘yiiwebBadRequestHttpException’ with message ‘Unable to verify your data submission
於是查看源代碼,yii 2.0 在 我們繼承的頂級 controller 中,有下列屬性
/** * @var boolean whether to enable CSRF validation for the actions in this controller. * CSRF validation is enabled only when both this property and [[Request::enableCsrfValidation]] are true. */ public $enableCsrfValidation = true;
106 行的 beforeAction 內,對他做了處理
``` public function beforeAction($action) { if (parent::beforeAction($action)) { if ($this->enableCsrfValidation && Yii::$app->getErrorHandler()->exception === null && !Yii::$app->getRequest()->validateCsrfToken()) { throw new BadRequestHttpException(Yii::t('yii', 'Unable to verify your data submission.')); } return true; } else { return false; } }
```
於是找到錯誤原因:
yii 2.0 內,對 CSRF 攻擊做了處理,會對 post 提交的數據做 token 驗證,而微信 post 到我們服務器的代碼中,沒有帶上這個 token ,所以會驗證失敗
解決方法:
1、在我們的控制器里面,加上這行屬性,設置為 false
public $enableCsrfValidation = false;
你還可以直接修改頂層控制器的 $enableCsrfValidation ,但是不推薦這樣做!
2、關於如何在單獨的action中關閉Csrf驗證
由於Yii2
Controller
Csrf
驗證是在beforeAction
中完成的,所以在action
中指定$this->enableCsrfValidation = false;
不能實現局部關閉Csrf。
如何實現
新建一個Behavior
<?php use Yii; use yii\base\ActionEvent; use yii\base\Behavior; use yii\web\Controller; class NoCsrf extends Behavior { public $actions = []; public $controller; public function events() { return [Controller::EVENT_BEFORE_ACTION => 'beforeAction']; } public function beforeAction($event) { $action = $event->action->id; if(in_array($action, $this->actions)){ $this->controller->enableCsrfValidation = false; } } }
然后在Controller中添加Behavior
?php public function behaviors() { return [ 'csrf' => [ 'class' => NoCsrf::className(), 'controller' => $this, 'actions' => [ 'action-name' ] ] ]; }
這樣就實現了在action
中關閉Csrf
而不是在整個Controller
中關閉。