【问题标题】:AntiForgeryToken Expiration Blank PageAntiForgeryToken 过期空白页
【发布时间】:2019-10-11 04:48:15
【问题描述】:

我将 IdentityServer4 与 ASP.NET Core 2.2 一起使用。在登录后方法上,我应用了 ValidateAntiForgeryToken。通常在登录页面上坐 20 分钟到 2 小时后,然后尝试登录它会产生一个空白页面。

如果您查看 Postman 控制台,您会收到 400 Bad Request 消息。然后,我将 AntiForgery 选项上的 Cookie Expiration 设置为 90 天。我能够让页面最多停留 6 个小时并且仍然登录。然而,大约 8 小时后(一夜之间),我在尝试登录后再次收到空白页面。

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login
services.AddAntiforgery(options =>
{
    options.Cookie.Expiration = TimeSpan.FromDays(90);
});

我希望能够在登录页面上停留 90 天,这是 cookie 的持续时间,但这不起作用。如何让 AntiforgeryToken 的 cookie 持续整个 90 天或我设置的任何时间并且不会超时或过期?有没有办法捕获这个错误并将用户重定向回登录方法?

【问题讨论】:

  • 里面不仅有cookie,还有token。并且 MS 在设置它的 ttl 时有一个开放的issue。但据我了解,默认情况下,只要浏览器保持会话,它就应该存在......但您可以使用他们的开放代码进行检查:)
  • 关于改变行为:您可以覆盖过滤器并重定向到具有相同名称的操作,但使用 HttpGet 而不是返回 400 状态
  • 我能够在验证失败后使用以下命令控制结果:await this._antiforgery.IsRequestValidAsync(HttpContext);。目前这对我来说非常有用,而不是使用内置属性。
  • 很高兴它对您有用,但总的来说,最好在任何地方使用一些通用方法,您可以通过将您的实现包装到一个新属性中来完成工作,然后在下面将其描述为任何人的答案否则正在寻找类似的解决方案...
  • 为什么需要AntiForgeryToken?你在这里违背了 AntiForgeryToken 的目的,你最好没有一个,而不是拥有这么长的时间。

标签: c# asp.net-core identityserver4 antiforgerytoken


【解决方案1】:

2021 年更新

由于 ASP.Net Core 3.0 MS decided 使 ValidateAntiforgeryTokenAuthorizationFilter 内部。现在我们必须复制粘贴their code,才能导出。但很可能我们不需要。要更改结果行为,我们需要测试IAntiforgeryValidationFailedResult 的上下文并按照此example 中的描述进行操作。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Core.Infrastructure;
using Microsoft.AspNetCore.Mvc.Filters;

namespace BasicWebSite.Filters
{
    public class RedirectAntiforgeryValidationFailedResultFilter : IAlwaysRunResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            if (context.Result is IAntiforgeryValidationFailedResult result)
            {
                context.Result = 
                    new RedirectResult("http://example.com/antiforgery-redirect");
            }
        }

        public void OnResultExecuted(ResultExecutedContext context)
        { }
    }
}

然后在控制器内:

// POST: /Antiforgery/LoginWithRedirectResultFilter
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[TypeFilter(typeof(RedirectAntiforgeryValidationFailedResultFilter))]
public string LoginWithRedirectResultFilter(LoginViewModel model)
{
    return "Ok";
}

涵盖 .net core 2.2 的原始答案

另一个使用默认实现的实现,包括所有预检查、日志记录等。它仍然是AuthorizationFilter,因此可以防止任何进一步的操作执行。唯一的区别是它会触发HttpGet 到相同的 url 而不是默认的 400 响应,一种 Post/Redirect/Get 模式实现。

public class AnotherAntiForgeryTokenAttribute : TypeFilterAttribute
{
    public AnotherAntiForgeryTokenAttribute() : base(typeof(AnotherAntiforgeryFilter))
    {
    }
}


public class AnotherAntiforgeryFilter:ValidateAntiforgeryTokenAuthorizationFilter,
    IAsyncAuthorizationFilter
{
    public AnotherAntiforgeryFilter(IAntiforgery a, ILoggerFactory l) : base(a, l)
    {
    }

    async Task IAsyncAuthorizationFilter.OnAuthorizationAsync(
        AuthorizationFilterContext ctx)
    {
        await base.OnAuthorizationAsync(ctx);

        if (ctx.Result is IAntiforgeryValidationFailedResult)
        {
            // the next four rows are optional, just illustrating a way
            // to save some sensitive data such as initial query
            // the form has to support that
            var request = ctx.HttpContext.Request;
            var url = request.Path.ToUriComponent();
            if (request.Form?["ReturnUrl"].Count > 0)
                url = $"{url}?ReturnUrl={Uri.EscapeDataString(request.Form?["ReturnUrl"])}";

            // and the following is the only real customization
            ctx.Result = new LocalRedirectResult(url);
        }
    }
}

【讨论】:

  • 我将对此进行测试,稍后再与您联系。我们决定在验证失败时不使用 ReturnUrl,因为它可能存在风险。我现在只是返回一个带有消息的视图。
  • 这很好用。我将使用您的解决方案,因为它使用默认的 AntiForgery 验证。感谢您的帮助。
  • 请问ValidateAntiforgeryTokenAuthorizationFilter 的命名空间是什么。我正在使用 asp.net core 5,它似乎没有找到那个类。什么是必需的“使用”声明?
  • 事情发生了变化。更新了答案。
  • 感谢您的更新。但是现在,我如何将它应用于剃须刀页面?因为我需要将它应用到登录页面,这是一个剃须刀页面,而且它似乎(防伪)不能像应用到 MVC 控制器那样应用到剃须刀页面
【解决方案2】:

这是我的最终解决方案。我使用 IAntifogery 依赖注入添加了一个属性。

public class CustomValidationAttribute : ActionFilterAttribute
{
    private IAntiforgery _antiForgery { get; }

    public CustomValidationAttribute(IAntiforgery antiforgery)
    {
        _antiForgery = antiforgery;
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var isRequestValid = await this._antiForgery.IsRequestValidAsync(context.HttpContext);
        if (!isRequestValid)
        {
            //Add Code here if token is not valid

            return;         
        }

        await next();
    }
}

将属性添加到也使用 [HttpPost] 的控制器方法中

[TypeFilter(typeof(CustomValidationAttribute))]

【讨论】:

  • 给了你我的投票,并添加了替代方案。可能会简单一点,但总的来说没关系
【解决方案3】:

对 d_f 代码 https://stackoverflow.com/a/56383473/841898 稍作修改,我们只是在 ModelState 中添加错误,而不是页面重定向。然后我们在模型状态摘要中显示。

public class CustomAntiForgeryTokenAttribute : TypeFilterAttribute
{
    public CustomAntiForgeryTokenAttribute() : base(typeof(AnotherAntiforgeryFilter))
    {
    }
}


public class AnotherAntiforgeryFilter : ValidateAntiforgeryTokenAuthorizationFilter,
    IAsyncAuthorizationFilter
{
    public AnotherAntiforgeryFilter(IAntiforgery a, ILoggerFactory l) : base(a, l)
    {
    }

    async Task IAsyncAuthorizationFilter.OnAuthorizationAsync(
        AuthorizationFilterContext ctx)
    {
        await base.OnAuthorizationAsync(ctx);

        if (ctx.Result is IAntiforgeryValidationFailedResult)
        {
            ctx.ModelState.AddModelError("Token", "Validation Token Expired. Please try again");
            ctx.Result = null;

        }
    }
}

【讨论】:

    【解决方案4】:

    我们正在使用 .netcore 3.1/razor 页面并希望将错误置于模型状态而不是 400 错误请求。根据 Rodney 和 Petr J 的回答进行编辑,我们首先全局禁用验证(因为默认情况下启用自动验证),然后使用页面过滤器手动验证。不是最有效的,但感觉比尝试捕获和还原AntiforgeryValidationFailedResult 更直接。

    配置服务:

    services.AddRazorPages()
            .AddMvcOptions(options =>
            {
                // Note: This seems to apply globally to all razor areas vs .AddRazorPages()
                
                // First remove default validation which returns 400 Bad Request
                //https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0#overriding-the-default-order
                options.Filters.Add(new Microsoft.AspNetCore.Mvc.IgnoreAntiforgeryTokenAttribute() { Order = 1001 });
    
                // Then add back a page filter, runs on all pages and areas (not tested on anything except razor pages; confirm odata, api, mvc)
                // Option 1 Type activated: instance for each request, DI
                // https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0#dependency-injection
                options.Filters.Add(typeof(Filters.AntiforgeryValidationModelStateOverridePageFilter));
                
            })
                
    

    然后是自定义过滤器:

    public class AntiforgeryValidationModelStateOverridePageFilter : IAsyncPageFilter {
    
        private readonly IAntiforgery _defaultAntiforgery;
        public AntiforgeryValidationModelStateOverridePageFilter(IAntiforgery defaultAntiforgery)
        {
            _defaultAntiforgery = defaultAntiforgery;
        }
    
        public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
        {
            var isRequestValid = _defaultAntiforgery.IsRequestValidAsync(context.HttpContext).Result;
    
            if (!isRequestValid)
            {
                //Add Code here if token is not valid
                context.ModelState.AddModelError("", Data.Constants.UserMessages.UserMessageErrorAntiforgery);
            }
    
            return Task.CompletedTask;
        }
    
        public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context,
                                                      PageHandlerExecutionDelegate next)
        {
            // Do post work.
            await next.Invoke();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-30
      • 2013-03-11
      相关资源
      最近更新 更多