Laravel的ajax POST请求中出现CSRF令牌不匹配的问题

204

我正在尝试通过 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不匹配...


请查看以下内容:https://dev59.com/77Dla4cB1Zd3GeqP61qd - Prateek
你应该在你的Ajax代码中添加成功和错误。错误会展示问题。 https://dev59.com/BVcO5IYBdhLWcg3whiBx - reza jafari
请使用以下链接Laravel官方网站,了解有关CSRF(跨站请求伪造)和X-CSRF-Token的详细信息。 - Jerin Stephen
26个回答

346

解决"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>

5
这是更好的解决方案,因为您可以在.js文件内使用它。 - Adam
如果 "global:false" 怎么办? - Michel
7
CSRF在每次调用后如何更新?第一次调用正常运行,随后的调用由于CSRF令牌失败而无法成功。 - Jjsg08
@zarpio 我正在用同样的方法进行,但是我的 ajax 调用中出现了令牌不匹配的错误。请帮我解决一下。 - Sachin Sarola
谢谢你的回答。我猜你可以创建一个全局的js变量,而不是通过jquery访问DOM。例如:<script> const csrf_token = '{{ csrf_token() }}'; </script>。这样你就可以全局访问并跳过对DOM的访问。 - Jorge Miguel Sanchez

326

你需要在ajax请求中添加数据。我希望这样会起作用。

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

59
如果 ajax 函数位于 .js 文件中怎么办? - Brane
5
不适用于Laravel 5.7。zarpio的答案是正确的。 - Omar Murcia
4
@Brane将令牌作为参数发送到函数中。 - Abdelalim Hassouna
这在 Laravel 5.8 中不起作用。它仍然显示令牌不匹配。请查看下面的答案,以获取简单的解决方案。 - Gjaa
Laravel在JSON请求后会更改CSRF令牌吗?您需要将新的令牌发送到主页面吗? - davefrassoni
我在使用ajax成功发送了令牌,但是出现了419令牌不匹配的错误。https://prnt.sc/199a0hj https://prnt.sc/199aim3 - Sachin Sarola

52

我刚刚在ajax调用中添加了headers:

  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);
}

在routes.php文件中

Route::post('ajax', 'AjaxController@call');
Laravel 8^
Route::post('ajax', [AjaxController::class, 'call']);

是的,我想这就是正确的解决方案。$.ajaxSetup() 会对每个请求进行全局配置。 - srijan lama

38

我认为将令牌放在表单中,并通过ID获取该令牌是更好的选择。

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

而jQuery:

var data = {
        "_token": $('#token').val()
    };
这样,你的 JS 就不需要放在 Blade 文件里了。

19
如果您正在使用模板文件,则可以将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);
           }
        }
    });

完成此操作后,您应该在ajax请求中获得200状态码


13

请注意,有一个X-XSRF-TOKEN cookie以方便使用。像Angular等框架默认设置它。在文档中检查 https://laravel.com/docs/5.7/csrf#csrf-x-xsrf-token 您可能希望使用它。

最好的方法是使用meta标签,以防cookie被禁用。

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

这里推荐使用 meta 方式(您可以以任何方式放置该字段,但meta比较好):

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

请注意使用decodeURIComponent(),它将从uri格式解码,这是用于存储cookie的格式。[否则,在laravel中你将得到一个无效的负载异常]。

在文档中检查关于csrf cookie的部分: 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

这里是读取 cookie 的函数:

   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;
    }

13

在包含令牌的meta元素上添加一个id

<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

9

在主文件中,您必须包含以下行:

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

在调用AJAX时,您必须实现CSRF令牌。

$.ajax({
url:url,
data:{
 _token:"{{ csrf_token() }}"
},
success:function(result){
 //success message after the controller is done..
}
})

5
如果您正在使用jQuery发送AJAX请求,将以下代码添加到所有视图中:
$( 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 cookie,并且我们会在提交之前自动将它附加到所有的AJAX请求中。

如果有其他函数或jQuery插件可以实现同样的功能,您可以替换getCookie函数。


3
在你的主页面(someViewsName.blade.php)中,声明一个全局变量。
<script>
    var token = "{{ csrf_token() }}";
</script>

<script src="/path/to/your_file.js"></script>

然后,在你的文件.js中

$.ajax({
        type: "post",
        url: "http://your.url/end/point",
        data: {
                _token:token,
                data:your_data,
              },
        dataType: "json",
        success: function (response) {
            // code some stuff
        }
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接