【问题标题】:Use AuthorizeAttribute without triggering a URL redirect on failure?使用 AuthorizeAttribute 而不在失败时触发 URL 重定向?
【发布时间】:2020-06-08 06:51:16
【问题描述】:

目前在 ASP.NET Core MVC 中,当使用 ConfigureApplicationCookie 和 Authorize 属性时,任何失败都会导致重定向到 AccessDenied 路径。

这给错误报告带来了挑战,因为最终用户无法看到最初请求的 URL。这也使故障排除变得更加困难,因为页面刷新只会导致重新加载访问被拒绝的页面(而不尝试重新授权。)

是否可以配置中间件在不重定向的情况下返回拒绝访问状态码页,类似于 UseStatusCodePagesWithReExecute?

【问题讨论】:

  • 覆盖授权(覆盖和覆盖一些你不应该用.net做的,因为它不是开源的)
  • ASP.NET Core(我们使用的)是开源的。您需要通过覆盖“授权”来解释更多您的意思 - 听起来您是在建议编写我们自己的授权中间件,这是我想要避免的。
  • 我的意思是,如果您查看帐户控制器上的登录方法,则会覆盖身份验证过程,您会看到如果未授权它会查看用户,它将重定向

标签: asp.net-mvc asp.net-core


【解决方案1】:
    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
    /* set if here if log in is not successful*/
    if (!result.Succeeded)
    {
       logger.log("stupid hacker");
    }
       if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}
as you can see in the lines when if method is fired and correct it redirect user to another page so what you want is redirect to current page that has already called authorization there you will get stackoverflow (wahahaha) there doing that will be wrong right? so what you do is put logger system in the if before redirect and you have every want to log and then redirect  

【讨论】:

    【解决方案2】:

    我们最终得到的解决方案相当笨拙,但只要您使用 Cookie auth 中间件,它就可以工作:

    services.ConfigureApplicationCookie(options =>
    {
        options.Events = new CookieAuthenticationEvents
        {
            OnRedirectToAccessDenied = async ctx =>
            {
                ctx.Response.StatusCode = 401;
                ctx.Response.ContentType = "text/html";
                var service = ctx.HttpContext.RequestServices.GetService(typeof(IViewRenderService)) as IViewRenderService;
                await ctx.Response.WriteAsync(await service.RenderToStringAsync("Errors/Unauthorized", null), Encoding.ASCII);
            }
        };
    });
    

    ViewRenderService 是这样的(可以在其他地方找到几个示例):

    public class ViewRenderService : IViewRenderService
    {
        private readonly IRazorViewEngine _razorViewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;
        private readonly HttpContext _httpContext;
    
        public ViewRenderService(IRazorViewEngine razorViewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider,
            IHttpContextAccessor httpContextAccessor)
        {
            _razorViewEngine = razorViewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
            _httpContext = httpContextAccessor.HttpContext;
        }
    
        public async Task<string> RenderToStringAsync(string viewName, object model)
        {
            var actionContext = new ActionContext(_httpContext, _httpContext.GetRouteData(), new ActionDescriptor());
    
            using var sw = new StringWriter();
            var viewResult = _razorViewEngine.FindView(actionContext, viewName, true);
    
            if (viewResult.View == null)
            {
                throw new ArgumentNullException($"{viewName} does not match any available view");
            }
    
            var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
            {
                Model = model
            };
    
            var viewContext = new ViewContext(
                actionContext,
                viewResult.View,
                viewDictionary,
                new TempDataDictionary(_httpContext, _tempDataProvider),
                sw,
                new HtmlHelperOptions()
            );
    
            await viewResult.View.RenderAsync(viewContext);
            return sw.ToString();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-11
      • 2022-01-21
      • 2019-10-07
      • 1970-01-01
      • 2017-04-24
      相关资源
      最近更新 更多