โทเค็น Laravel csrf ไม่ตรงกันสำหรับ ajax POST Request


122

ฉันกำลังพยายามลบข้อมูลจากฐานข้อมูลผ่าน ajax

HTML:

@foreach($a as $lis)
  //some code
  <a href="#" class="delteadd" id="{{$lis['id']}}">Delete</a>
  //click action perform on this link                  
@endforeach

รหัส ajax ของฉัน:

$('body').on('click', '.delteadd', function (e) {
e.preventDefault();
//alert('am i here');
if (confirm('Are you sure you want to Delete Ad ?')) {
    var id = $(this).attr('id');
    $.ajax({
        method: "POST",
        url: "{{url()}}/delteadd",
        }).done(function( msg ) {
        if(msg.error == 0){
            //$('.sucess-status-update').html(msg.message);
            alert(msg.message);
        }else{
            alert(msg.message);
            //$('.error-favourite-message').html(msg.message);
        }
    });
} else {
    return false;
}
});

นี่คือแบบสอบถามของฉันในการดึงข้อมูลจากฐานข้อมูล ...

$a = Test::with('hitsCount')->where('userid', $id)->get()->toArray();

แต่เมื่อฉันคลิกที่ลบข้อมูลลิงก์ไม่ได้ถูกลบและแสดง csrf_token ไม่ตรงกัน ...


ตรวจสอบสิ่งนี้: stackoverflow.com/questions/53684928/…
Prateek

คุณควรเพิ่มความสำเร็จและข้อผิดพลาดให้กับรหัส ajax ของคุณ ข้อผิดพลาดจะแสดงปัญหา stackoverflow.com/questions/45668337/…
reza jafari

คำตอบ:


190

คุณต้องเพิ่มข้อมูลในคำขอ ajax ของคุณ ฉันหวังว่ามันจะได้ผล

data: {
        "_token": "{{ csrf_token() }}",
        "id": id
        }

33
จะเกิดอะไรขึ้นถ้าฟังก์ชัน ajax อยู่ใน.jsไฟล์?
Brane

2
ใช้ไม่ได้กับ Laravel 5.7 คำตอบของ zarpio ถูกต้อง
Omar Murcia

1
@Brane ส่งโทเค็นเป็นพารามิเตอร์ในฟังก์ชัน
Abdelalim Hassouna

สิ่งนี้ใช้ไม่ได้ใน Laravel 5.8 มันยังบอกว่าโทเค็นไม่ตรงกัน ตรวจสอบคำตอบของฉันด้านล่างเพื่อดูวิธีง่ายๆ
Gjaa

laravel เปลี่ยนโทเค็น csrf หลังจากคำขอ json หรือไม่ คุณต้องส่งใหม่ไปที่หน้าหลักหรือไม่?
davefrassoni

202

วิธีที่ดีที่สุดในการแก้ปัญหา "X-CSRF-TOKEN" นี้คือการเพิ่มโค้ดต่อไปนี้ในเค้าโครงหลักของคุณและทำการโทร ajax ของคุณต่อไปตามปกติ:

ในส่วนหัว

<meta name="csrf-token" content="{{ csrf_token() }}" />

ในสคริปต์

<script type="text/javascript">
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});
</script>

6
ขอบคุณคน นี่เป็นวิธีแก้ปัญหาที่กลมกว่า! วิธีนี้คุณจะตั้งค่าเพียงครั้งเดียวและหลังจากนั้นก็เขียนโค้ด $ .ajax ปกติ
pkid169

4
นี่เป็นทางออกที่ดีกว่าเพราะคุณสามารถใช้ใน.jsไฟล์ได้
Adam

3
คำตอบที่ดีที่สุดจนถึงตอนนี้ ขอบคุณ.
Paul Denisevich

จะเกิดอะไรขึ้นถ้า "global: false"?
Michel

3
csrf สามารถอัพเดตได้อย่างไรหลังการโทรแต่ละครั้ง? การโทรครั้งแรกใช้งานได้ดีการโทรย่อยล้มเหลวเนื่องจากโทเค็น CSRF
Jjsg08

28

ฉันคิดว่าควรใส่โทเค็นในแบบฟอร์มดีกว่าและรับโทเค็นนี้ด้วยรหัส

<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">

และ JQUery:

var data = {
        "_token": $('#token').val()
    };

ด้วยวิธีนี้ JS ของคุณไม่จำเป็นต้องอยู่ในไฟล์เบลดของคุณ


27

ฉันเพิ่งเพิ่มheaders:ในการโทร ajax:

  headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},

ในมุมมอง:

<div id = 'msg'>
     This message will be replaced using Ajax. Click the button to replace the message.
</div>

{{ Form::submit('Change', array('id' => 'ajax')) }}

ฟังก์ชัน ajax:

<script>
 $(document).ready(function() {
    $(document).on('click', '#ajax', function () {
      $.ajax({
         type:'POST',
         url:'/ajax',
         headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
         success:function(data){
            $("#msg").html(data.msg);
         }
      });
    });
});
</script>

ในตัวควบคุม:

public function call(){
    $msg = "This is a simple message.";
    return response()->json(array('msg'=> $msg), 200);
}

ใน route.php

Route::post('ajax', 'AjaxController@call');

1
การเพิ่มส่วนหัวในการโทร ajax ช่วยฉันได้
Chaudhry Waqas

1
นี่เป็นคำตอบที่ดีที่สุดเพราะไม่ต้องการให้ JavaScript อยู่ในไฟล์เบลด (เว้นแต่คุณต้องการให้อยู่ในไฟล์เบลด แต่จะมีการแสดงผลทุกครั้งที่มีคนเข้ามาที่หน้านั้น)
Zachary Weixelbaum

12

หากคุณกำลังใช้ไฟล์เทมเพลตคุณสามารถใส่metaแท็กไว้ในส่วนหัวsection(หรือชื่ออะไรก็ได้) ซึ่งมีmetaแท็กของคุณ

@section('head')
<meta name="csrf_token" content="{{ csrf_token() }}" />
@endsection

สิ่งต่อไปคุณต้องใส่headersแอตทริบิวต์ให้กับคุณajax(ในตัวอย่างของฉันฉันใช้datatableกับการประมวลผลฝั่งเซิร์ฟเวอร์:

"headers": {'X-CSRF-TOKEN': $('meta[name="csrf_token"]').attr('content')}

นี่คือdatatableตัวอย่าง ajax แบบเต็ม:

$('#datatable_users').DataTable({
        "responsive": true,
        "serverSide": true,
        "processing": true,
        "paging": true,
        "searching": { "regex": true },
        "lengthMenu": [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] ],
        "pageLength": 10,
        "ajax": {
            "type": "POST",
            "headers": {'X-CSRF-TOKEN': $('meta[name="csrf_token"]').attr('content')},
            "url": "/getUsers",
            "dataType": "json",
            "contentType": 'application/json; charset=utf-8',
            "data": function (data) {
                console.log(data);
            },
            "complete": function(response) {
                console.log(response);
           }
        }
    });

หลังจากทำสิ่งนี้คุณควรได้รับคำขอ200 statusของคุณajax


6

ทราบว่ามีคุกกี้ X-XSRF-TOKEN ที่ตั้งไว้เพื่อความสะดวก Framework เช่น Angular และอื่น ๆ ตั้งค่าเป็นค่าเริ่มต้น ตรวจสอบสิ่งนี้ในเอกสารhttps://laravel.com/docs/5.7/csrf#csrf-x-xsrf-token คุณอาจต้องการใช้

วิธีที่ดีที่สุดคือใช้เมตาในกรณีที่คุกกี้ถูกปิดใช้งาน

    var xsrfToken = decodeURIComponent(readCookie('XSRF-TOKEN'));
    if (xsrfToken) {
        $.ajaxSetup({
            headers: {
                'X-XSRF-TOKEN': xsrfToken
            }
        });
    } else console.error('....');

ต่อไปนี้เป็นวิธีเมตาที่แนะนำ (คุณสามารถใส่ฟิลด์ได้ทุกวิธี แต่เมตาเงียบดี):

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});   

สังเกตการใช้งานdecodeURIComponent()จะถอดรหัสจากรูปแบบ uri ซึ่งใช้ในการจัดเก็บคุกกี้ [มิฉะนั้นคุณจะได้รับข้อยกเว้น payload ที่ไม่ถูกต้องใน laravel]

ดูส่วนเกี่ยวกับคุกกี้ csrf ในเอกสารเพื่อตรวจสอบ: https://laravel.com/docs/5.7/csrf#csrf-x-csrf-token

นอกจากนี้วิธีที่ laravel (bootstrap.js) ตั้งค่าสำหรับ axios โดยค่าเริ่มต้น:

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
} 

resources/js/bootstrap.jsคุณสามารถไปตรวจสอบ

และที่นี่อ่านฟังก์ชันคุกกี้:

   function readCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
       }
        return null;
    }

6

เพิ่มidไปยังmetaองค์ประกอบที่ถือโทเค็น

<meta name="csrf-token" id="csrf-token" content="{{ csrf_token() }}">

จากนั้นคุณจะได้รับใน Javascript ของคุณ

$.ajax({
  url : "your_url",
  method:"post",
  data : {
    "_token": $('#csrf-token')[0].content  //pass the CSRF_TOKEN()
  },  
  ...
});

แก้ไข: วิธีที่ง่ายกว่าโดยไม่ต้องเปลี่ยนmetaสาย

data : { 
    _token: "{{ csrf_token() }}" 
}

หรือ

data : { 
    _token: @json(csrf_token()), 
}

ขอบคุณข้อมูล @ martin-hartmann


2
หากคุณไม่ต้องการเพิ่ม id ให้ใช้: $ ("[name = csrf-token]"). attr ("content") แทน มันจะดึงองค์ประกอบที่ถูกต้องโดยใช้แอตทริบิวต์ชื่อ
Pedro Sousa

1
นอกจากนี้ยังสามารถโฆษณาในช่องข้อมูล: data: {"_token": "{{csrf_token ()}}" // ส่งผ่าน CSRF_TOKEN ()}
Martin Hartmann

3

หากคุณใช้ jQuery เพื่อส่ง AJAX Posts ให้เพิ่มรหัสนี้ในมุมมองทั้งหมด:

$( document ).on( 'ajaxSend', addLaravelCSRF );

function addLaravelCSRF( event, jqxhr, settings ) {
    jqxhr.setRequestHeader( 'X-XSRF-TOKEN', getCookie( 'XSRF-TOKEN' ) );
}

function getCookie(name) {
    function escape(s) { return s.replace(/([.*+?\^${}()|\[\]\/\\])/g, '\\$1'); };
    var match = document.cookie.match(RegExp('(?:^|;\\s*)' + escape(name) + '=([^;]*)'));
    return match ? match[1] : null;
}

Laravel เพิ่มคุกกี้ XSRF ให้กับคำขอทั้งหมดและเราจะผนวกเข้ากับคำขอ AJAX ทั้งหมดโดยอัตโนมัติก่อนที่จะส่ง

คุณสามารถแทนที่ฟังก์ชัน getCookie ได้หากมีฟังก์ชันอื่นหรือปลั๊กอิน jQuery เพื่อทำสิ่งเดียวกัน


1

สำหรับ Laravel 5.8 การตั้งค่าเมตาแท็ก csrf สำหรับเลย์เอาต์ของคุณและการตั้งค่าส่วนหัวของคำขอสำหรับ csrf ในการตั้งค่า ajax จะไม่ทำงานหากคุณใช้ ajax เพื่อส่งแบบฟอร์มที่มีช่อง_tokenป้อนข้อมูลที่สร้างโดยเอนจินการเทมเพลตใบมีด Laravel อยู่แล้ว

คุณต้องรวมโทเค็น csrf ที่สร้างไว้แล้วจากแบบฟอร์มพร้อมกับคำขอ ajax ของคุณเนื่องจากเซิร์ฟเวอร์คาดหวังไว้ไม่ใช่ในเมตาแท็ก

ตัวอย่างเช่นนี่คือลักษณะของช่อง_tokenป้อนข้อมูลที่สร้างโดย Blade:

<form>
    <input name="_token" type="hidden" value="cf54ty6y7yuuyyygytfggfd56667DfrSH8i">
    <input name="my_data" type="text" value="">
    <!-- other input fields -->
</form>

จากนั้นคุณส่งแบบฟอร์มของคุณด้วย ajax ดังนี้:

<script> 
    $(document).ready(function() { 
        let token = $('form').find('input[name="_token"]').val();
        let myData = $('form').find('input[name="my_data"]').val();
        $('form').submit(function() { 
            $.ajax({ 
                type:'POST', 
                url:'/ajax', 
                data: {_token: token, my_data: myData}
                // headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}, // unnecessary 
                // other ajax settings
            }); 
            return false;
        }); 
    }); 
</script>

โทเค็น csrf ในส่วนหัวเมตามีประโยชน์เฉพาะเมื่อคุณส่งแบบฟอร์มโดยไม่มีช่อง_tokenป้อนข้อมูลที่สร้างด้วย Blade



1

ในกรณีที่เซสชันของคุณหมดอายุคุณสามารถใช้สิ่งนี้เพื่อเข้าสู่ระบบอีกครั้ง

$(document).ajaxComplete(function(e, xhr, opt){
  if(xhr.status===419){
    if(xhr.responseJSON && xhr.responseJSON.message=='CSRF token mismatch.') window.location.reload();
  }
});

0

ฉันมีข้อผิดพลาดนี้จริงและไม่สามารถหาวิธีแก้ไขได้ ฉันลงเอยด้วยการไม่ร้องขอ ajax ฉันไม่รู้ว่าปัญหานี้เกิดจากการที่นี่เป็นโดเมนย่อยบนเซิร์ฟเวอร์ของฉันหรืออะไร นี่คือ jquery ของฉัน

            $('#deleteMeal').click(function(event) {
                var theId = $(event.currentTarget).attr("data-mealId");
                  $(function() {
                    $( "#filler" ).dialog({
                      resizable: false,
                      height:140,
                      modal: true,
                      buttons: {
                      "Are you sure you want to delete this Meal? Doing so will also delete this meal from other users Saved Meals.": function() {
                           $('#deleteMealLink').click();
//                         jQuery.ajax({
//                              url : 'http://www.mealog.com/mealtrist/meals/delete/' + theId,
//                              type : 'POST',
//                              success : function( response ) {
//                                  $("#container").replaceWith("<h1 style='color:red'>Your Meal Has Been Deleted</h1>");
//                              }
//                          });
                        // similar behavior as clicking on a link
                           window.location.href = 'http://www.mealog.com/mealtrist/meals/delete/' + theId;
                          $( this ).dialog( "close" );
                        },
                        Cancel: function() {
                          $( this ).dialog( "close" );
                        }
                      }
                    });
                  });
                });

ดังนั้นฉันจึงตั้งค่าจุดยึดเพื่อไปที่ API ของฉันแทนที่จะทำคำขอโพสต์ซึ่งเป็นสิ่งที่ฉันคิดว่าแอปพลิเคชันส่วนใหญ่ทำ

  <p><a href="http://<?php echo $domain; ?>/mealtrist/meals/delete/{{ $meal->id }}" id="deleteMealLink" data-mealId="{{$meal->id}}" ></a></p>

0

คุณควรรวมฟิลด์โทเค็น CSRF (การปลอมแปลงคำขอข้ามไซต์) ที่ซ่อนอยู่ในแบบฟอร์มเพื่อให้การป้องกัน CSRFมิดเดิลแวร์สามารถตรวจสอบคำขอได้

Laravel จะสร้าง "โทเค็น" CSRF โดยอัตโนมัติสำหรับแต่ละเซสชันของผู้ใช้ที่ใช้งานอยู่ซึ่งจัดการโดยแอปพลิเคชัน โทเค็นนี้ใช้เพื่อตรวจสอบว่าผู้ใช้ที่ได้รับการพิสูจน์ตัวตนนั้นเป็นผู้ที่ส่งคำขอไปยังแอปพลิเคชันจริงๆ

ดังนั้นเมื่อทำการร้องขอ ajax คุณจะต้องส่งโทเค็น csrfผ่านพารามิเตอร์ข้อมูล นี่คือโค้ดตัวอย่าง

var request = $.ajax({
    url : "http://localhost/some/action",
    method:"post",
    data : {"_token":"{{ csrf_token() }}"}  //pass the CSRF_TOKEN()
  });

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.