پیاده سازی XSRF با web API و Angular در MVC

هدف از ارائه این مقاله ، آموزش چگونگی کارکرد پیاده سازی ساختار anti XSRF در MVC می باشد . که این پیاده سازی را ما با کمک AngularJS و Web API انجام خواهیم داد ، و در ادامه با ارائه کدهایی ، مسئله را به طور کامل برای شما بسط خواهیم داد .

پیاده سازی XSRF با web API و Angular در MVC

یکی از پروژه های محبوب ما ، یک برنامه Single Page است که یک منطقه جدید برای ما بود ، بنابراین ما نیاز داشتیم که یکسری چیزها را خودمان ابداع  کنیم . یکی از این موارد محافظت از XSRF بود . با  MVC/Razor این کار بسیار راحت بود فقط کافی بود که ما از Html.AntiForgeryToken() استفاده کنیم و در ادامه همه چیز سر جایش قرار میگرفت .

اول از همه اجازه دهید تا در مورد چگونگی کارکرد پیاده سازی ساختار anti XSRF در MVC صحبت کنیم .
زمانی که 
Html.AntiForgeryToken در Razor ظاهر میشود ، در آن زمان دو اتفاق رخ می دهد :
در HTML یک عنصر input وجود دارد که مخفی میباشد که نصفِ token در آن ذخیره شده است و همچنین در ادامه یک cookie به آن ضمیمه می شود . بعد از این کار ، زمانی که کاربر فرم را submit میکند ، token ای که در فیلد مخفی قرار داشت در بدنه درخواست قرار می گیرد و cookie هم تحت عنوان cookie میرود  

در سمت سرور ، کلاس AntiForgeryToken ، عمل validation را برای token ها انجام میدهد که مشخص کند درست است یا نه .  حال ، اجازه دهید سوییچ کنیم روی SPA، در این مورد ما سمت سرور نداریم و token تحت عنوان markup در سمت کاربر ساخته خواهند شد . 

راه حلی که ما با آن بالا آمدیم در زیر خواهید دید :
ما یک web API endpoint ایجاد کردیم که از کلاس معمولیِ AntiForgeryToken برای ایجاد tokenها استفاده می‌کند و این دو token را به صفحه درخواست کننده ارسال میکند تحت عنوان Cookie .
ما Token را با دستور AngularJS ارائه خواهیم داد و یک interceptor در Http header به token ضمیمه می‌شود . بعد از این ، یک فیلتر ویژه در سمت سرور token ، validationها را انجام میدهد .  
 

[HttpGet]
[Route("antiforgerytoken")]
public HttpResponseMessage GetAntiForgeryToken()
{
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
 
    HttpCookie cookie = HttpContext.Current.Request.Cookies["xsrf-token"];
 
    string cookieToken;
    string formToken;
    AntiForgery.GetTokens(cookie == null ? "" : cookie.Value, out cookieToken, out formToken);
 
    AntiForgeryTokenModel content = new AntiForgeryTokenModel
    {
        AntiForgeryToken = formToken
    };
 
    response.Content = new StringContent(JsonConvert.SerializeObject(content), Encoding.UTF8, "application/json");
 
    if (!string.IsNullOrEmpty(cookieToken))
    {
        response.Headers.AddCookies(new[]
        {
            new CookieHeaderValue("xsrf-token", cookieToken)
            {
                Expires = DateTimeOffset.Now.AddMinutes(10),
                Path = "/"
            }
        });
    }
 
    return response;
}


مرحله بعد دستور AngularJS است . endpoint را فراخوانی کرده و token را تحت عنوان یک input مخفی ارائه میدهد ، کاری که MVC در حالت عادی انجام میدهد . 
 

(function() {
    'use strict';
 
    function antiForgeryDirectiveController(appService) {
        var directive = this;
 
        directive.antiForgeryToken = '';
 
        directive.activate = function () {
            appService.getAntiForgeryToken().then(function(data) {
                directive.antiForgeryToken = data.antiForgeryToken;;
            });
        };
 
        directive.activate();
    }
 
    function antiForgeryTokenDirective() {
        return {
            scope: {},
            controllerAs: 'directive',
            template: '<input id="__antiForgeryToken" name="antiForgeryToken" type="hidden" value="{{directive.antiForgeryToken}}" />',
            controller: [ 'appService', antiForgeryDirectiveController ]
        }
    }
 
    angular.module('demoApp').directive('antiforgerytoken', antiForgeryTokenDirective);
})();

ما کار دیگری در سمت کاربر داریم . اگر HTML شامل یک عنصر input مخفی باشد ، پس نیاز است که یک Html header به درخواست اضافه کنیم . برای این کار ما از یک interceptor استفاده میکنیم :
 

(function() {
    'use strict';
 
    function antiForgeryInterceptor() {
        return {
            request: function($config) {
                var antiForgeryTokenField = document.getElementById('__antiForgeryToken');
                if (antiForgeryTokenField) {
                    var xsrfToken = antiForgeryTokenField.value;
                    $config.headers['XSRF-TOKEN'] = xsrfToken;
                }
 
                return $config;
            }
        };
    }
 
    angular.module('demoApp').service('antiForgeryInterceptor', antiForgeryInterceptor);
})();


مرحله آخر validation سمت سرور است :
 

public sealed class ValidateAntiForgeryTokenFilter : ActionFilterAttribute
{
    private const string XsrfHeader = "XSRF-TOKEN";
    private const string XsrfCookie = "xsrf-token";
 
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        HttpRequestHeaders headers = actionContext.Request.Headers;
        IEnumerable xsrfTokenList;
 
        if (!headers.TryGetValues(XsrfHeader, out xsrfTokenList))
        {
            actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
            return;
        }
 
        string tokenHeaderValue = xsrfTokenList.First();
 
        CookieState tokenCookie = actionContext.Request.Headers.GetCookies().Select(c => c[XsrfCookie]).FirstOrDefault();
 
        if (tokenCookie == null)
        {
            actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
            return;
        }
 
        try
        {
            AntiForgery.Validate(tokenCookie.Value, tokenHeaderValue);
        }
        catch (HttpAntiForgeryException)
        {
            actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
        }
    }
}


بعد از این ، ما میتوانیم از این فیلتر تحت عنوان صفت اصلی ValidateAntyForgeryToken استفاده کنیم .

دانلود نسخه ی PDF این مطلب