如何在CodeIgniter 4中确保所有AJAX请求持续验证CSRF Token
解决CodeIgniter中AJAX登录请求CSRF验证403错误
问题原因分析
- URL不匹配:表单action使用
validate_login(下划线命名),但AJAX请求URL为validate-login(中划线命名),若CSRFStrict配置为true,URI不匹配会直接触发CSRF验证失败。 - 重复生成Token导致不一致:Controller中使用
csrf_hash()会强制生成新Token,与CodeIgniter自动 regenerate的Token冲突,导致前端发送的Token与服务器Session中存储的Token不匹配。 - 验证失败时未正确设置状态:原Controller中字段验证失败时,未将
success设为0,可能导致前端逻辑异常。
解决方案
步骤1:统一AJAX请求URL
将AJAX请求URL改为与表单action一致的下划线命名:
$("#form").validate({ // ... 原有规则与提示 submitHandler : function(form,e){ e.preventDefault(); var form_data = new FormData(form); $.ajax({ url : SITE_URL+"validate_login", // 统一为下划线命名 type : "POST", contentType: false, cache: false, processData:false, data : form_data, success : function(response){ $('.txt_csrfname').val(response.data.token); if(response.success == "1"){ $("#alert-message").css("display","none"); window.location.href= SITE_URL+"Dashboard"; }else{ console.log(response.data.token); $("#alert-message").css("display",""); } }, error : function(){ $("#alert-message").css("display",""); $("#alert-message .alert").html("<strong>Error!</strong> Request failed."); } }); } });
步骤2:修改Controller获取Token的方式
使用getCSRFHash()获取当前Session中的Token,避免手动重复生成:
public function validate_login(){ $request = service('request'); $requestArr = $request->getPost(); $responseArr = array("success"=>"1","data"=>array(),"message"=>""); $data = array(); // 获取当前Session中的CSRF Token,而非强制生成新Token $data['token'] = $request->getCSRFHash(); $ipaddress = $this->request->getIPAddress(); $rules = [ 'username' => 'required|valid_email', 'password' => 'required' ]; if($this->validate($rules)){ $userArr = $this->Auth->login($requestArr['username']); if(!empty($userArr)) { $verify_password = password_verify($requestArr['password'],$userArr->password); if($verify_password){ $responseArr['success'] = "1"; $responseArr['message'] = "Loged in successfully."; $this->session->set([ 'tbl_users_id'=>$userArr->tbl_users_id, 'full_name'=>$userArr->full_name, 'username'=>$userArr->username, 'email'=>$userArr->email ]); $logArr = array( 'user_id' => $userArr->tbl_users_id, 'module' => $this->module, 'module_id' => $userArr->tbl_users_id, 'action' => "LOGIN", 'ip_address' => $ipaddress, 'remarks' => "User Loged In", 'raw_data'=> "" ); insert_log($logArr); }else{ $responseArr['success'] = "0"; $responseArr['message'] = "In-valid username or password."; } }else{ $responseArr['success'] = "0"; $responseArr['message'] = "In-valid username or password."; } }else{ $responseArr['success'] = "0"; // 补充设置失败状态 $responseArr['message']= "Required Fields are missing"; } $responseArr['data']= $data; output($responseArr); }
步骤3:可选 - 同步更新Token名称(若配置动态变更)
如果你的CSRF Token名称不是固定值,可在Controller中返回名称,前端同步更新input的name属性:
// Controller中添加返回Token名称 $data['token_name'] = $request->getCSRFTokenName();
// JS中更新name属性 success : function(response){ $('.txt_csrfname').attr('name', response.data.token_name); $('.txt_csrfname').val(response.data.token); // ... 原有逻辑 }
验证要点
- 确保请求URI与生成Token时的URI完全一致(包括大小写、符号),尤其是
CSRFStrict设为true时。 - 每次提交FormData时,都会重新从表单获取最新的Token值,无需额外处理。
内容的提问来源于stack exchange,提问作者Snehalkumar Vasava
相关产品推荐
相关产品推荐

