Asp.net-Mvc-4

帶有 SPA 架構的 ValidateAntiForgeryToken

  • February 23, 2021

我正在嘗試為 Hot Towel SPA 申請設置註冊和登錄。我基於asp.net 單頁應用程序模板創建了 SimpleMembershipFilters 和 ValidateHttpAntiForgeryTokenAttribute 。

你如何得到

@Html.AntiForgeryToken()

在 Durandal SPA 模式下工作的程式碼。

目前我有一個register.html

<section>
   <h2 data-bind="text: title"></h2>

   <label>Firstname:</label><input data-bind="value: firstName" type="text"  />
   <label>Lastname:</label><input data-bind="value: lastName" type="text"  />
   <label>Email:</label><input data-bind="value: emailAddress" type="text"  />
   <label>Company:</label><input data-bind="value: company" type="text"  />
   <br />
   <label>Password:</label><input data-bind="value: password1" type="password" />
   <label>Re-Enter Password:</label><input data-bind="value: password2" type="password" />
   <input type="button" value="Register" data-bind="click: registerUser" class="btn" />
</section>

註冊.js

define(['services/logger'], function (logger) {
   var vm = {
       activate: activate,
       title: 'Register',
       firstName: ko.observable(),
       lastName: ko.observable(),
       emailAddress: ko.observable(),
       company: ko.observable(),
       password1: ko.observable(),
       password2: ko.observable(),
       registerUser: function () {
           var d = {
               'FirstName': vm.firstName,
               'LastName': vm.lastName,
               'EmailAddress': vm.emailAddress,
               'Company': vm.company,
               'Password': vm.password1,
               'ConfirmPassword': vm.password2
           };
           $.ajax({
               url: 'Account/JsonRegister',
               type: "POST",
               data: d ,
               success: function (result) {
               },
               error: function (result) {
               }
           });
       },
   };


   return vm;

   //#region Internal Methods
   function activate() {
       logger.log('Login Screen Activated', null, 'login', true);
       return true;
   }
   //#endregion
});

在 $ajax 呼叫中如何傳遞 AntiForgeryToken?另外我如何創建令牌?

我會閱讀這篇關於如何使用 javascript 使用防偽令牌的文章。這篇文章是為 WebApi 編寫的,但如果你願意,它可以很容易地應用於 MVC 控制器。

簡短的回答是這樣的:在您的 cshtml 視圖中:

<script>
   @functions{
       public string TokenHeaderValue()
       {
           string cookieToken, formToken;
           AntiForgery.GetTokens(null, out cookieToken, out formToken);
           return cookieToken + ":" + formToken;                
       }
   }

   $.ajax("api/values", {
       type: "post",
       contentType: "application/json",
       data: {  }, // JSON data goes here
       dataType: "json",
       headers: {
           'RequestVerificationToken': '@TokenHeaderValue()'
       }
   });
</script>

然後在您的 asp.net 控制器中,您需要像這樣驗證令牌:

void ValidateRequestHeader(HttpRequestMessage request)
{
   string cookieToken = "";
   string formToken = "";

   IEnumerable<string> tokenHeaders;
   if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
   {
       string[] tokens = tokenHeaders.First().Split(':');
       if (tokens.Length == 2)
       {
           cookieToken = tokens[0].Trim();
           formToken = tokens[1].Trim();
       }
   }
   AntiForgery.Validate(cookieToken, formToken);
}

您想在標頭中傳遞它的原因是,如果您在請求的查詢字元串或正文內的 ajax 呼叫中將其作為參數數據參數傳遞。然後,您將更難獲得適用於所有不同場景的防偽令牌。因為您必須反序列化正文然後找到令牌。在標題中,它非常一致且易於檢索。


**為光線編輯

這是一個動作過濾器範例,您可以使用它來為 Web api 方法賦予屬性,以驗證是否提供了防偽令牌。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Helpers;
using System.Web.Http.Filters;
using System.Net.Http;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Threading;

namespace PAWS.Web.Classes.Filters
{
   public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
   {
       public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
       {
           if (actionContext == null)
           {
               throw new ArgumentNullException("HttpActionContext");
           }

           if (actionContext.Request.Method != HttpMethod.Get)
           {
               return ValidateAntiForgeryToken(actionContext, cancellationToken, continuation);
           }

           return continuation();
       }

       private Task<HttpResponseMessage> FromResult(HttpResponseMessage result)
       {
           var source = new TaskCompletionSource<HttpResponseMessage>();
           source.SetResult(result);
           return source.Task;
       }

       private Task<HttpResponseMessage> ValidateAntiForgeryToken(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
       {
           try
           {
               string cookieToken = "";
               string formToken = "";
               IEnumerable<string> tokenHeaders;
               if (actionContext.Request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
               {
                   string[] tokens = tokenHeaders.First().Split(':');
                   if (tokens.Length == 2)
                   {
                       cookieToken = tokens[0].Trim();
                       formToken = tokens[1].Trim();
                   }
               }
               AntiForgery.Validate(cookieToken, formToken);
           }
           catch (System.Web.Mvc.HttpAntiForgeryException ex)
           {
               actionContext.Response = new HttpResponseMessage
               {
                   StatusCode = HttpStatusCode.Forbidden,
                   RequestMessage = actionContext.ControllerContext.Request
               };
               return FromResult(actionContext.Response);
           }
           return continuation();
       }
   }
}

引用自:https://stackoverflow.com/questions/15416264