在ASP.Net MVC中使用@Html.AntiForgeryToken()提供CSRF令牌的替代方法

在ASP.Net MVC中使用@Html.AntiForgeryToken()提供CSRF令牌的替代方法,asp.net,asp.net-mvc-4,angular,csrf-protection,Asp.net,Asp.net Mvc 4,Angular,Csrf Protection,我正在使用ASP.Net MVC后端开发angular 2应用程序。为此,index.cshtml包含了@Html.AntiForgeryToken(){ 让originalXsrfElement=document.querySelector('[name=\uu RequestVerificationToken]'); 让身体=反应; 如果(正文){ 让helperElem=document.createElement('div'); helperElem.innerHTML=body; 让h

我正在使用
ASP.Net MVC
后端开发
angular 2
应用程序。为此,
index.cshtml
包含了
@Html.AntiForgeryToken()
现在,由于用户信息用于在后端生成令牌,因此我必须在某些情况下关闭令牌。描述了该问题

现在,因为我不想在我的应用程序中重新加载整个页面,所以我必须使用变通方法。为此,我在后端创建了一个局部视图,该视图使用
@Html.AntiForgeryToken()发出此输入作为
ActionResult
使用以下简单方法:

public ActionResult GetAntiForgery()
{
    return PartialView("~/Views/Home/AntiForgery.cshtml");
}
getAntiForgery(url:string) {
        return this._http.get(url)
            .map((response:any) => {
                let originalXsrfElement = <HTMLInputElement>document.querySelector('[name=__RequestVerificationToken]');
                let body = response._body;

                if(body) {
                    let helperElem = document.createElement('div');
                    helperElem.innerHTML = body;
                    let helperInputElem = <HTMLInputElement>helperElem.firstChild;
                    originalXsrfElement.value = helperInputElem.value;
                }

                return response;
            });
    }
登录/注销后,我从前端调用此函数,并替换现有
反伪造输入元素的值,如下所示:

public ActionResult GetAntiForgery()
{
    return PartialView("~/Views/Home/AntiForgery.cshtml");
}
getAntiForgery(url:string) {
        return this._http.get(url)
            .map((response:any) => {
                let originalXsrfElement = <HTMLInputElement>document.querySelector('[name=__RequestVerificationToken]');
                let body = response._body;

                if(body) {
                    let helperElem = document.createElement('div');
                    helperElem.innerHTML = body;
                    let helperInputElem = <HTMLInputElement>helperElem.firstChild;
                    originalXsrfElement.value = helperInputElem.value;
                }

                return response;
            });
    }
getAntiForgery(url:string){
返回此。\u http.get(url)
.map((响应:任意)=>{
让originalXsrfElement=document.querySelector('[name=\uu RequestVerificationToken]');
让身体=反应;
如果(正文){
让helperElem=document.createElement('div');
helperElem.innerHTML=body;
让helperInputElem=helperElem.firstChild;
originalXsrfElement.value=helperInputElem.value;
}
返回响应;
});
}
我甚至不能告诉你我最讨厌的是什么。我不喜欢提出额外的请求(但我们不要在这里深入讨论),但对我来说更可怕的是,我必须请求一些东西,取回一个html字符串,并从中提取令牌字符串

如果我是后端人员,我会杀死前端人员(我…),因为他们甚至考虑创建额外的局部视图,创建额外的方法,并且总是在登录/注销时执行两个请求,而不是一个

有更好的办法吗?例如,我想调用一个方法,该方法只需发出一个带有适当标记的
JSON
,而不是
HTML片段
。更好的是,在后端现有的
JsonResult
方法上,我想添加新的
CSRF令牌作为属性

我不是一个后端架构师,所以我通常的做法可能有一些错误,但我的后端同事并不介意我在那里做什么,所以应该不会太遥远


非常感谢您的提示。

您可以在操作中使用
Antifforgery.GetToken
设置返回的JSON的某些属性:

string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
json.RequestVerificationToken = cookieToken + ":" + formToken;
然后,将其传递回下一个AJAX请求的标题中:

$.ajax("/my/awesome/url", {
    type: "post",
    contentType: "application/json",
    data: { ... },
    dataType: "json",
    headers: {
        'RequestVerificationToken': requestVerificationToken
    }
});
因为Cookie不再处理它,所以不能只使用标准的
ValidateAntiForgeryToken
属性。您需要手动检查标题并对其进行验证。但是,您应该能够创建一个可以替代使用的自定义属性:

AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ValidateAntiForgeryTokenFromHeaderAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var request = filterContext.HttpContext.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);
    }
}
AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,AllowMultiple=false,Inherited=true)]
公共密封类ValidateAntiForgeryTokenFromHeaderAttribute:FilterAttribute,IAAuthorizationFilter
{
授权时的公共无效(AuthorizationContext filterContext)
{
var request=filterContext.HttpContext.request;
字符串cookieToken=“”;
字符串formToken=“”;
IEnumerable标记头;
if(request.Headers.TryGetValues(“RequestVerificationToken”,out-tokenHeaders))
{
string[]tokens=tokenHeaders.First().Split(“:”);
if(tokens.Length==2)
{
cookieToken=tokens[0]。Trim();
formToken=tokens[1].Trim();
}
}
防伪。验证(cookieToken,formToken);
}
}
然后,只需使用
[ValidateAntiForgeryTokenFromHeader]
来装饰您的操作即可


[改编自

的代码看起来这个问题可能会有所帮助(基本上是将防伪放在ajax标题而不是隐藏字段中):对不起,我看不出这对解决我的问题有什么帮助。问题不同。“反CSRF和ajax”链接文章中的部分似乎正是你想要的:现在我明白你想告诉我什么,我认为我们彼此误解了。请参阅我对下面答案的评论,以澄清我的问题所在。我没有得到的是
@Html.AntiForgeryToken()
call插入一个html代码段,一个以令牌作为值的输入。这是否已经设置了cookie,或者为什么您的建议不再适用于cookie?我只想获得
@html.AntiForgeryToken()的值
并将此字符串值作为json响应发送,而不是发送一个视图。对于我来说,您的方法似乎有点过火,仅仅是为了这个小小的“更改”。例如“前端:服务器,给我那个令牌字符串”。“服务器:不,对不起,我只能给您一个html元素…”.等等,什么?;)通常,通过AJAX请求命中的任何东西都不支持cookies。设计良好的API将基于REST,REST是无状态的(没有会话,没有cookies)。Web API肯定不支持它。