web-dev-qa-db-fra.com

Activer le jeton antiforgery avec ASP.NET Core et JQuery

J'utilise JQuery avec ASP.NET Core 1.0.1 et j'ai l'appel Ajax:

$("#send-message").on("submit", function (event) {
  event.preventDefault();
  var $form = $(this);   
  $.ajax({
    url: "api/messages",
    data: JSON.stringify($form.serializeToJSON()),
    dataType: "json",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json"
    },
    type: "post"
  })
  .done(function (data, status, xhr) { })
  .fail(function (xhr, status, error) { });

Pour l'action ASP.NET Core:

[HttpPost("messages")]
public async Task<IActionResult> Post([FromBody]MessagePostApiModelModel model) {
   // Send message
}

Le formulaire est dans une vue partagée et se présente comme suit:

<form id="send-question" method="post">
  <textarea name="content"></textarea>
  <button class="button" type="submit">Enviar</button>
</form>

Lorsque je soumets le formulaire, le message d'erreur s'affiche:

Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The required antiforgery header value "RequestVerificationToken" is not present.

Comment puis-je activer AntiForgeryToken d'ASP.NET Core avec les appels JQuery Ajax?

METTRE &AGRAVE; JOUR

J'ai besoin d'ajouter l'asp-controller et l'asp-action suivants au formulaire:

<form asp-controller="QuestionApi" asp-action="Post" id="send-question" method="post">
</form>

Cela générera le jeton antiforgery. Et je devais ajouter manuellement le jeton aux en-têtes de l'appel JQuery comme suit:

  headers: {
    "Accept": "application/json",
    "Content-Type": "application/json",
    "RequestVerificationToken": $form.find("input[name='af_token']").val()
  },

Y a-t-il une meilleure manière de faire cela?

Comment résoudre ce problème quand il n'y a pas de formulaire et que je n'ai qu'un tag A qui, lorsqu'il est cliqué, appelle Ajax? Puis-je générer un jeton antiforgery commun sur l'en-tête de page à utiliser par tous les appels ajax de cette page?

12
Miguel Moura

la réponse de mode777 nécessite juste un petit ajout pour que cela fonctionne (je l'ai essayé):

$(document).ajaxSend(function(e, xhr, options) {
    if (options.type.toUpperCase() == "POST") {
        var token = $form.find("input[name='af_token']").val();
        xhr.setRequestHeader("RequestVerificationToken", token);
    }
});

En fait, si vous soumettez également en utilisant Ajax, vous n'avez pas du tout besoin d'utiliser de formulaire ..__ Mettez ceci dans votre _layout:

 <span class="AntiForge"> @Html.AntiForgeryToken() </span>

Ensuite, vous récupérez le jeton en l'ajoutant à votre javascript:

$(document)
   .ajaxSend(function (event, jqxhr, settings) {
        if (settings.type.toUpperCase() != "POST") return;
        jqxhr.setRequestHeader('RequestVerificationToken', $(".AntiForge" + " input").val())
})

@HtmlAntiForgeryToken génère un champ de saisie masqué avec le jeton antiforgery, identique à celui utilisé lors de l'utilisation d'un formulaire. Le code ci-dessus le trouve en utilisant le sélecteur de classe pour sélectionner la plage, puis obtient le champ de saisie à l'intérieur de celui-ci pour collecter le jeton et l'ajouter en tant qu'en-tête.

14
Pieter van Kampen

Remarque: cette réponse s'applique à ASP.NET Core 2.0. Cela peut ne pas convenir aux anciennes versions.

Voici ce que j'ai fait après avoir fouillé dans le code source d'aspnet pendant un court instant:

public static class HttpContextExtensions
{
    public static string GetAntiforgeryToken(this HttpContext httpContext)
    {
        var antiforgery = (IAntiforgery)httpContext.RequestServices.GetService(typeof(IAntiforgery));
        var tokenSet = antiforgery.GetAndStoreTokens(httpContext);
        string fieldName = tokenSet.FormFieldName;
        string requestToken = tokenSet.RequestToken;
        return requestToken;
    }
}

Vous pouvez l'utiliser dans une vue comme celle-ci:

<script>
    var data = {
        yourData: "bla",
        "__RequestVerificationToken": "@Context.GetAntiforgeryToken()"
    };
    $.post("@Url.Action("ActionName")", data);
</script>

Vous pouvez modifier la méthode d'extension pour renvoyer également le nom du champ, sous la forme de votre choix, e. g. un fragment JSON.

8
ygoe

Vous pouvez enregistrer un événement global ajax qui ajoutera l'en-tête à tous les appels ajax qui ne sont pas GET par ceci:

$(document).ajaxSend(function(e, xhr, options) {
    if (options.type.toUpperCase() != "GET") {
        xhr.setRequestHeader("RequestVerificationToken", token);
    }
});
3
mode777

Dans Asp.Net Core, vous pouvez demander le jeton directement, comme documenté :

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf    
@functions{
    public string GetAntiXsrfRequestToken()
    {
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    }
}

Et utilisez-le en javascript:

function DoSomething(id) {
    $.post("/something/todo/"+id,
               { "__RequestVerificationToken": '@GetAntiXsrfRequestToken()' });
}

Vous pouvez ajouter le filtre global recommandé, comme documenté :

services.AddMvc(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
})
2
Ruard van Elburg

En plus de ygoe's answer , si vous souhaitez transmettre un jeton XSRF en tant qu'en-tête, par exemple X-XSRF-Token:

var ajax = {
    url: "/users",
    data: data,
    type: "post",

    // ...
};

var antiForgeryToken = $("input[name=__RequestVerificationToken]").val();
if (antiForgeryToken) {
    ajax.headers = {};
    ajax.headers["X-XSRF-Token"] = antiForgeryToken;
};

$.ajax(ajax);

alors vous devrez également spécifier l'option antiforgery respective:

public void ConfigureServices(IServiceCollection services)
{
    // your services to inject are also configured here ...

    services.AddAntiforgery(options => options.HeaderName = "X-XSRF-Token");
    services.AddMvc();
}

Vous pouvez ensuite utiliser l'attribut standard ValidateAntiForgeryToken pour valider la demande:

[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Users(UserModel user)
{
    // ...
}
0
Dmitry Karpenko