使用授权属性而不在失败时触发 URL 重定向?



目前在核心MVC ASP.NET,当使用ConfigureApplicationCookie和Authorization属性时,任何失败都将导致重定向到AccessDened路径。

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

是否可以将中间件配置为返回拒绝访问状态代码页而不重定向,类似于 UseStatusCodePagesWithReExecute?

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  

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

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();
}
}

最新更新