2016-04-14 22 views
9

私は、ValidateAntiForgeryTokenのAjaxバージョンを再作成しようとしています。以前のバージョンのMVCではこれを行う方法についてのブログ記事は多数ありますが、最新のMVC 6ではコードは関連しています。ただし、Cookieをフォーム値と比較するのではなく、__RequestVerificationTokenのCookieとヘッダーを検証するようにしてください。私はMVC 6.0.0-rc1-final、dnx451フレームワークを使用しています。すべてのMicrosoft.Extensionsライブラリは1.0.0-rc1-finalです。AspNetコアMVCを使用したAjaxリクエストのValidateAntiForgeryToken

最初はValidateAntiForgeryTokenAttributeを継承していましたが、ソースコードを見て、ヘッダを見るために独自の実装を返す必要がありました。このように

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
public class ValidateAjaxAntiForgeryTokenAttribute : Attribute, IFilterFactory, IFilterMetadata, IOrderedFilter 
{ 
    public int Order { get; set; } 
    public bool IsReusable => true; 
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) 
    { 
     return serviceProvider.GetRequiredService<ValidateAjaxAntiforgeryTokenAuthorizationFilter>(); 
    } 
} 

、私はその後、ValidateAntiforgeryTokenAuthorizationFilter

public class ValidateAjaxAntiforgeryTokenAuthorizationFilter : IAsyncAuthorizationFilter, IAntiforgeryPolicy 
{ 
    private readonly IAntiforgery _antiforgery; 
    private readonly ILogger _logger; 
    public ValidateAjaxAntiforgeryTokenAuthorizationFilter(IAntiforgery antiforgery, ILoggerFactory loggerFactory) 
    { 
     if (antiforgery == null) 
     { 
      throw new ArgumentNullException(nameof(antiforgery)); 
     } 
     _antiforgery = antiforgery; 
     _logger = loggerFactory.CreateLogger<ValidateAjaxAntiforgeryTokenAuthorizationFilter>(); 
    } 
    public async Task OnAuthorizationAsync(AuthorizationContext context) 
    { 
     if (context == null) 
     { 
      throw new ArgumentNullException(nameof(context)); 
     } 
     if (IsClosestAntiforgeryPolicy(context.Filters) && ShouldValidate(context)) 
     { 
      try 
      { 
       await _antiforgery.ValidateRequestAsync(context.HttpContext); 
      } 
      catch (AjaxAntiforgeryValidationException exception) 
      { 
       _logger.LogInformation(1, string.Concat("Ajax Antiforgery token validation failed. ", exception.Message)); 
       context.Result = new BadRequestResult(); 
      } 
     } 
    } 
    protected virtual bool ShouldValidate(AuthorizationContext context) 
    { 
     if (context == null) 
     { 
      throw new ArgumentNullException(nameof(context)); 
     } 
     return true; 
    } 
    private bool IsClosestAntiforgeryPolicy(IList<IFilterMetadata> filters) 
    { 
     // Determine if this instance is the 'effective' antiforgery policy. 
     for (var i = filters.Count - 1; i >= 0; i--) 
     { 
      var filter = filters[i]; 
      if (filter is IAntiforgeryPolicy) 
      { 
       return object.ReferenceEquals(this, filter); 
      } 
     } 
     Debug.Fail("The current instance should be in the list of filters."); 
     return false; 
    } 
} 

の自分自身のバージョンを作ったしかし、私はIAntiforgeryPolicyが含まれている適切なNugetパッケージと名前空間を見つけることができません。私はGitHub上のインターフェースを見つけている間に、どのパッケージを見つけるのですか?

私の次の試みは、IAntiforgery注入の代わりに、DefaultAntiforgeryを私自身のAjaxAntiforgeryに置き換えました。

public class AjaxAntiforgery : DefaultAntiforgery 
{ 
    private readonly AntiforgeryOptions _options; 
    private readonly IAntiforgeryTokenGenerator _tokenGenerator; 
    private readonly IAntiforgeryTokenSerializer _tokenSerializer; 
    private readonly IAntiforgeryTokenStore _tokenStore; 
    private readonly ILogger<AjaxAntiforgery> _logger; 
    public AjaxAntiforgery(
     IOptions<AntiforgeryOptions> antiforgeryOptionsAccessor, 
     IAntiforgeryTokenGenerator tokenGenerator, 
     IAntiforgeryTokenSerializer tokenSerializer, 
     IAntiforgeryTokenStore tokenStore, 
     ILoggerFactory loggerFactory) 
    { 
     _options = antiforgeryOptionsAccessor.Value; 
     _tokenGenerator = tokenGenerator; 
     _tokenSerializer = tokenSerializer; 
     _tokenStore = tokenStore; 
     _logger = loggerFactory.CreateLogger<AjaxAntiforgery>(); 
    } 
} 

私はCreateLogger<T>()ためILoggerFactoryには、一般的な方法がないので、私はアウト失速はるか前にこれを得ました。 DefaultAntiforgeryのソースコードはMicrosoft.Extensions.Optionsですが、Nugetパッケージの名前空間は見つかりません。 Microsoft.Extensions.OptionsModelが存在しますが、それはちょうどIOptions<out TOptions>インターフェイスをもたらします。そしてアクションのみのために - 私は認証フィルターが動作し得るか、または私はIAntiforgeryの新しい実装を取得し、どこかどのように私はそれを使用する依存性注入とそれを登録しないと、このすべてをフォローアップする

私はAjax要求を受け入れるでしょうか?

答えて

3

私は同様の状況で、角度のあるPOSTとMVC6とのインターフェースを取り合い、以下のことを考え出しました。

セキュリティトークンをMVCの不正防止検証サブシステムに取得し、AngleのJSON形式のポストバックデータをMVCモデルに変換するという2つの問題があります。

私は、Startup.Configure()に挿入されたカスタムミドルウェアを介して最初の手順を処理します。ミドルウェアのクラスは非常に単純です:あなたはStartup.Configure()メソッド内の以下の行をパイプラインにこれ​​を挿入

public static class UseAngularXSRFExtension 
{ 
    public const string XSRFFieldName = "X-XSRF-TOKEN"; 

    public static IApplicationBuilder UseAngularXSRF(this IApplicationBuilder builder) 
    { 
     return builder.Use(next => context => 
     { 
      switch(context.Request.Method.ToLower()) 
      { 
       case "post": 
       case "put": 
       case "delete": 
        if(context.Request.Headers.ContainsKey(XSRFFieldName)) 
        { 
         var formFields = new Dictionary<string, StringValues>() 
         { 
          { XSRFFieldName, context.Request.Headers[XSRFFieldName] } 
         }; 

         // this assumes that any POST, PUT or DELETE having a header 
         // which includes XSRFFieldName is coming from angular, so 
         // overwriting context.Request.Form is okay (since it's not 
         // being parsed by MVC's internals anyway) 
         context.Request.Form = new FormCollection(formFields); 
        } 

        break; 
      } 

      return next(context); 
     }); 
    } 
} 

app.UseAngularXSRF(); 

私はアプリの呼び出し前にこの権利をしました。 UseMVC()。

この拡張機能は、既存のフォームフィールドコレクションを上書きすることによって、XSRFヘッダーを既存のPOST、PUT、またはDELETEに転送します。それは私のデザインパターンに合っています - 私が書いたいくつかの角度コードから来ている場合、XSRFヘッダーがリクエストに含まれる唯一の時間ですが、それはあなたのものに合わないかもしれません。

また、XSRFフィールド名に正しい名前を使用するように偽造防止サブシステムを構成する必要があると思います(私はデフォルトが何であるか分かりません)。あなたは)(Startup.ConfigureServicesに次の行を挿入することでこれを行うことができます。

services.ConfigureAntiforgery(options => options.FormFieldName = UseAngularXSRFExtension.XSRFFieldName); 

私はラインservices.AddAntiforgery()の前にこの権利を挿入します。

XSRFトークンを要求ストリームに取り込む方法はいくつかあります。私は何をすると、ビューに以下を追加します:

 // the [FromBody] attribute on the model -- and a class model, rather than a 
     // single integer model -- are necessary so that MVC can parse the JSON-formatted 
     // text POSTed by angular 
     [HttpPost] 
     [ValidateAntiForgeryToken] 
     public IActionResult DeleteDataset([FromBody] DeleteSiteViewModel model) 
     { 
} 

...top of view... 
@inject Microsoft.AspNet.Antiforgery.IAntiforgery af 
...rest of view... 

...inside the angular function... 
      var postHeaders = { 
       'X-XSRF-TOKEN': '@(af.GetTokens(this.Context).FormToken)', 
       'Content-Type': 'application/json; charset=utf-8', 
      }; 

      $http.post('/Dataset/DeleteDataset', JSON.stringify({ 'siteID': siteID }), 
       { 
        headers: postHeaders, 
       }) 
...rest of view... 

第二部 - - JSONデータを変換するには、[FromBody]を使用してアクションメソッドにモデルクラスを飾るによって処理されます

[FromBody]はクラスインスタンスでのみ機能します。私が興味を持っているのは、単一の整数だけですが、私はまだ1つの整数プロパティしか含まないクラスをダミーにしなければなりませんでした。

これが役に立ちます。

0

Ajax呼び出しで偽造トークンを使用することは可能ですが、Apiを保護しようとしている場合は、代わりにアクセストークンを使用することをお勧めします。

Apiの認証としてCookieに格納されているIDトークンに頼っている場合は、Cookie認証がタイムアウトしてAjaxポストがログイン画面にリダイレクトされるタイミングを補正するコードを記述する必要があります。これはSPAやAngularアプリにとって特に重要です。

代わりにアクセストークンの実装を使用すると、アクセストークン(リフレッシュトークンを使用)をリフレッシュしてセッションを長くし、Cookieの泥棒がApisにアクセスするのを止めさせ、XSRFも停止します:)

アクセストークンの目的は、Web Apisのようなリソースを保護することです。

+1

コード例がありますか? – Grandizer

8

私も同様の問題がありました。 .NETでこれに関する変更があるかどうかはわかりませんが、当時は次の行をConfigureServicesメソッドStartup.csの行services.AddMvc()の前に追加しました。 Ajaxを介して送信されるAntiForgeryTokenを検証:

var token = $('input[type=hidden][name=__RequestVerificationToken]', document).val(); 

var request = $.ajax({ 
    data: { 'yourField': 'yourValue' }, 
    ... 
    headers: { 'RequestVerificationToken': token } 
}); 

はその後、ちょうどあなたの行動にネイティブ属性[ValidadeAntiForgeryToken]を使用します。

services.AddAntiforgery(options => 
{ 
    options.CookieName = "yourChosenCookieName"; 
    options.HeaderName = "RequestVerificationToken"; 
}); 

AJAX呼び出しは、以下のようなものになるだろう。

+0

ありがとうございます。 .Net Core 2 Razorページの使用 - 完璧な作業 – Gfw

関連する問題