web-dev-qa-db-fra.com

jQuery ajoute un jeton CSRF à toutes les données des demandes $ .post ()

Je travaille sur une application Laravel 5 pour laquelle la protection CSRF est activée par défaut pour toutes les demandes POST. J'aime cette sécurité supplémentaire, alors j'essaie de travailler avec.

Lors de la création d'une demande $.post() simple, j'ai reçu une erreur 'Illuminate\Session\TokenMismatchException' car l'entrée de formulaire requise _token était absente des données POST. Voici un exemple de demande $ .post en question:

var userID = $("#userID").val();
$.post('/admin/users/delete-user', {id:userID}, function() {
// User deleted
});

J'ai mon jeton CSRF stocké en tant que méta-champ dans mon en-tête et je peux facilement y accéder en utilisant:

var csrf_token = $('meta[name="csrf-token"]').attr('content');

Est-il possible d’ajouter cela aux données JSON sur toutes les demandes $.post() sortantes? J'ai essayé d'utiliser des en-têtes mais Laravel n'a pas semblé les reconnaître - 

var csrf_token = $('meta[name="csrf-token"]').attr('content');
alert(csrf_token);
$.ajaxPrefilter(function(options, originalOptions, jqXHR){
    if (options['type'].toLowerCase() === "post") {
        jqXHR.setRequestHeader('X-CSRFToken', csrf_token);
    }
});
21
NightMICU

Votre approche $.ajaxPrefilter en est une bonne. Vous n'avez pas besoin d'ajouter un en-tête, cependant; vous devez simplement ajouter une propriété à la chaîne data.

Les données sont fournies en tant que deuxième argument de $.post, puis formatées en tant que chaîne de requête (id=foo&bar=baz&...) avant que le préfiltre ne puisse accéder à l'option data. Ainsi, vous devez ajouter votre propre champ à la chaîne de requête:

var csrf_token = $('meta[name="csrf-token"]').attr('content');
$.ajaxPrefilter(function(options, originalOptions, jqXHR){
    if (options.type.toLowerCase() === "post") {
        // initialize `data` to empty string if it does not exist
        options.data = options.data || "";

        // add leading ampersand if `data` is non-empty
        options.data += options.data?"&":"";

        // add _token entry
        options.data += "_token=" + encodeURIComponent(csrf_token);
    }
});

Cela transformera id=userID en id=userID&_token=csrf_token.

24
apsillers

De la documentation de Laravel:

Vous pouvez, par exemple, stocker le jeton dans une balise "meta":

Une fois que vous avez créé la balise META, vous pouvez charger une bibliothèque telle que jQuery pour ajouter le jeton à tous les en-têtes de requête. Cela fournit simple, protection CSRF pratique pour vos applications basées sur AJAX:

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

Donc, par exemple, vous pouvez faire la demande comme ci-dessous.

Ajoutez cette balise meta à votre vue: 

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

Et ceci est un exemple de script avec lequel vous pouvez communiquer avec Laravel (envoie une demande lorsque vous cliquez sur un élément avec id = "some-id" et que vous pouvez voir la réponse dans un élément avec id = "result"):

<script type="text/javascript">
    $(document).ready(function(){

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

        $("#some-id").on("click", function () {
            var request;


            request = $.ajax({
                url: "/your/url",
                method: "POST",
                data:
                {
                    a: 'something',
                    b: 'something else',
                },
                datatype: "json"
            });

            request.done(function(msg) {
                $("#result").html(msg);
            });

            request.fail(function(jqXHR, textStatus) {
                $("#result").html("Request failed: " + textStatus);
            });
        });

    });
</script>
17
Kornel

En général, je suis d’accord avec le concept suggéré par Kornel, sauf une chose. 

Oui, la documentation de Laravel conseille d'utiliser $.ajaxSetup, mais ce n'est pas recommandé car cette méthode affecte toutes les requêtes ajax suivantes. Il est plus correct de définir les paramètres ajax pour chaque demande. Bien que vous puissiez redéfinir des choses:

Tous les appels Ajax ultérieurs utilisant une fonction quelconque utiliseront les nouveaux paramètres, à moins qu’ils ne soient remplacés par les appels individuels, jusqu’à la prochaine invocation de $ .ajaxSetup ().

Si vous utilisez $.ajax(), il est plus pratique d’utiliser la propriété data ou headers. Laravel autorise le jeton CSRF en tant que paramètre de requête ou en-tête.

Tout d'abord, vous ajoutez la balise META suivante dans la vue

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

Et faites ensuite une requête ajax dans les deux sens:

$.ajax({
    url: "/your/url",
    method: "POST",
    data:
    {
        a: 'something',
        b: 'something else',
        _token: $('meta[name="csrf-token"]').attr('content')
    },
    datatype: "json"
});

OR

$.ajax({
    url: "/your/url",
    method: "POST",
    data:
    {
        a: 'something',
        b: 'something else',
    },
    headers: 
    {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
    datatype: "json"
});
6
Alex Baklanov

La documentation Django sur CSRF donne un extrait de code Nice avec ajaxSetup pour ajouter automatiquement l’en-tête approprié à tous les types de demandes, le cas échéant:

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});
5
phk

il existe une méthode beaucoup plus simple pour ce faire, vous pouvez sérialiser les données comme avant l'envoi

<form method="post" id="formData">
 <input type="text" name="name" >
 <input type="hidden" name="_token" value="{{ csrf_token() }}">
 <input type="submit" id="sub" class="btn btn-default" value="Submit" />                            
</form>
<script>
    $(document).on('submit', '#formData', function (event) {
    event.preventDefault();
     var formData = $('#formData').serialize();
    $.ajax({
        url: url,
       type: "POST",
       data : formData,   
                success: function (result) {
                    console.log(result);                             
                }
           });
        });

    });
</script>

tout avec un nom attr sera mis à une requête et soumis 

0
c319113

Je pense que la solution ci-dessus pourrait ne pas fonctionner aussi bien. Quand tu fais:

var x; 
x + ""
// "undefined" + empty string coerces value to string 

Vous obtiendrez des données du type "undefined_token = xxx"

Lorsque vous utilisez la solution ci-dessus pour supprimer par exemple laravel, vous devez vérifier comme suit:

if (typeof options.data === "undefined")
    options.data = "";
else
    options.data += "&";
options.data = "_token=" + csrf_token;
0
Kamil Latosinski