【问题标题】:MVC 4 - AntiForgeryValidationToken only creating cookie for first requestMVC 4 - AntiForgeryValidationToken 仅为第一个请求创建 cookie
【发布时间】:2015-05-18 20:42:41
【问题描述】:

好的,这是我们昨天处理的一个奇怪案例。当我们的网站被加载时,这个表单的第一个请求会按预期工作。防伪验证通过,没有任何错误,一切都很好。但是,在首次成功使用后,其他浏览器请求会停止生成防伪令牌的 cookie 部分。所有进一步的发布尝试都将返回 500,表明 cookie 不存在。查看浏览器资源确认 cookie 不存在。我假设这是某种形式的配置问题,因为在我们的测试和开发环境中一切正常,但是一旦我们转移到我们的暂存环境,问题就会出现并且我已经没有想法了。

我有一个这样的控制器操作:

[HttpPost]
[ValidateAntiForgeryToken()]
public bool someFunction(string someParameter) {
    // Do things
    return success;
}

在我们的页面上,我有一个这样的设置

<form id="someForm">
    @Html.AntiForgeryToken()
    <input type="text" name="someField" />
</form>

它是这样连接起来的:

$("#someForm").On("submit", function(e) {
e.PreventDefault();
var url = "path/to/action";
var someFieldVal = $(this).Find('[name="someField"]').val();
var token = $(this).Find('[name="__RequestValidationToken"]');
var data = { '__RequestVerificationToken': token, 'someParameter': someFieldval };
$.ajax({
     type: "POST",
     url: url,
     data: data,
     success: function (response) {//do stuff}
     });
}

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-4 cookies


    【解决方案1】:

    对于 ajax 帖子,您将令牌放在标题而不是数据中。

    $.ajax(
        {
            type: "POST",
            url: url,
            headers: { "__RequestVerificationToken": token },
            data: data,
            success: function (response) {//do stuff}
    |;
    

    编辑

    我忘记了另一半。

    您说第二个浏览器帖子没有防伪 cookie。如果发生这种情况,那将是一个问题。但是,从您的代码示例来看,您的 ajax 帖子似乎不包括 cookie 而不是浏览器请求。

    cookie 是一个 HttpOnly cookie。这意味着(除其他外)javascript 无法访问它。如果您使用 ajax 发布表单,它将包含 cookie。但是,您的发布数据。所以它不包括防伪cookie,因为ajax不允许读取cookie并将cookie包含在帖子中。

    有些人会在每个页面上放置一个仅用于 ajax 帖子的表单。但是,我更喜欢发布数据而不是表单。所以我发布的后半部分是创建一个授权属性。此属性在标头中查找令牌,然后对其进行验证。

    这是创建我使用的授权属性的类。

    [AttributeUsage(AttributeTargets.Class)]
    public sealed class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            var request = filterContext.HttpContext.Request;
    
            //  Only validate POSTs
            if (request.HttpMethod == WebRequestMethods.Http.Post)
            {
                //  Ajax POSTs and normal form posts have to be treated differently when it comes
                //  to validating the AntiForgeryToken
                if (request.IsAjaxRequest())
                {
                    var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
    
                    var cookieValue = antiForgeryCookie != null
                        ? antiForgeryCookie.Value
                        : null;
    
                    AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
                }
                else
                {
                    new ValidateAntiForgeryTokenAttribute().OnAuthorization(filterContext);
                }
            }
        }
    }
    

    然后,您只需将属性添加到要验证防伪令牌的每个控制器。

    [ValidateAntiForgeryTokenOnAllPosts]
    public class SomeController : Controller
    {   }
    

    我认为这是我第一次学习如何做到这一点的网站:http://richiban.uk/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/

    【讨论】:

    • 好的,很公平,我将从这里开始调整我的代码,但这会导致验证令牌的 Cookie 部分的行为仅提供给第一个请求的浏览器页面?
    • 对不起,我忘记了另一半。我更新了答案以包含所有详细信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-23
    • 1970-01-01
    相关资源
    最近更新 更多