【问题标题】:Prevent direct url access to a razor page handler防止对剃刀页面处理程序的直接 url 访问
【发布时间】:2019-09-15 20:33:00
【问题描述】:

我已经制作了剃须刀页面来注销用户。当经过身份验证的用户访问路由/account/logout 时,我想向他显示带有成功消息的页面,如果用户是匿名的,那么页面是未经授权的。但是,不能通过直接 url 输入访问此成功页面。

以下代码运行良好,但任何人都可以导航到/account/logout/success,它充当普通页面(并且由于它不负责退出,因此可能会造成混淆)。

public class LogoutModel : CustomPageModel
{
    private readonly SignInManager _signInManager;

    public LogoutModel(SignInManager signInManager) => _signInManager = signInManager;

    public async Task<IActionResult> OnGetAsync()
    {
        if (_signInManager.IsSignedIn(User))
        {
            await _signInManager.SignOutAsync();
            return RedirectToPage("Logout", "Success");
        }

        return Unauthorized();
    }

    public void OnGetSuccess()
    {
    }
}

如何防止直接访问处理程序 OnGetSuccess?

【问题讨论】:

    标签: c# asp.net-core razor-pages asp.net-core-2.2


    【解决方案1】:

    请参阅this question,了解它是如何实现的。

    不确定我是否建议走这条路,因为它有点脏,而且解决方案非常复杂。您可以为登录/退出的用户返回不同的状态消息。

    注销.cshtml.cs

    public class LogoutModel : CustomPageModel
    {
        private readonly SignInManager _signInManager;
    
        public LogoutModel(SignInManager signInManager) => _signInManager = signInManager;
    
        public string StatusMessage { get; set; }
    
        public async Task<IActionResult> OnGetAsync()
        {
            if (_signInManager.IsSignedIn(User))
            {
                await _signInManager.SignOutAsync();
                StatusMessage = "Successfully logged out!";
                return Page();
            }
            else {
                StatusMessage = "Already logged out!";
                return Page();
            }
    
        }
    }
    

    注销.cshtml

    @page
    @model X.LogoutModel
    
    @Model.StatusMessage
    

    【讨论】:

    • 重定向到上下文状态的页面是我的问题,它们总是可以直接访问。假设用户登录并转到第 1 页(因为他可能已将其保存在历史记录中),页面会说它已成功将他注销,但事实并非如此,因为这没有通过调用 @987654324 的注销处理程序@.
    • 这就是为什么我建议 [Authorize] 到 Page1 类,需要登录才能访问它。或者,如果用户登录后访问,您可以重定向用户。
    • 您不能这样做,当您重定向到 Page1 时,他已不再经过身份验证。起初我使用return Page() 来显示包含成功消息的注销页面的内容。这将解决手头的问题,但会引入另一个问题,即向用户呈现内容,就好像他仍然经过身份验证(而他已经不是),直到他刷新页面。
    • 啊,我的错,所以这个替代方案是一个糟糕的建议。我已经编辑了我的答案并调整了代码以仅反映状态消息建议。
    【解决方案2】:

    我已经通过使用 Attribute 和 IPageFilter 检查Referer 标头来解决它。

    首先我创建了属性

    [AttributeUsage(AttributeTargets.Method)]
    public class ChildHandlerAttribute : Attribute
    {
    }
    

    然后我用它装饰了GetOnSuccess() 处理程序

    [ChildHandler]
    public void OnGetSuccess()
    {
    }
    

    然后我实现了检查子处理程序的Referer标头的过滤器。

    public class ChildHandlerAsyncPageFilter : IAsyncPageFilter
    {
        public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
        {
            var pageHandlerExecutedContext = await next();
    
            if (pageHandlerExecutedContext.HandlerMethod?.MethodInfo.GetCustomAttribute<ChildHandlerAttribute>() == null)
            {
                return;
            }
    
            var referrer = context.HttpContext.Request.Headers["Referer"].ToString();
            var request = pageHandlerExecutedContext.HttpContext.Request;
    
            if (!referrer.StartsWith($"{request.Scheme}://{request.Host}"))
            {
                pageHandlerExecutedContext.Result = new NotFoundResult();
            }
        }
    
        public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) => Task.CompletedTask;
    }
    

    最后,我在 Startup.cs

    中将过滤器添加到管道中
    services.AddMvc(options =>
    {
        options.Filters.Add<ChildHandlerAsyncPageFilter>();
    });
    

    【讨论】:

      【解决方案3】:

      在您的场景中,用户退出并作为匿名用户重定向。所以不能说这里只有某些用户可以访问这个操作。

      您应该通过其他方式解决此问题。例如,您可以在LogOutAction 的cookie 中设置值,然后签入SuccessAction。为了获得更高的安全性,请应用 MVC 用于 CSRF 的模式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-05
        • 2010-09-16
        • 2018-09-14
        • 1970-01-01
        • 1970-01-01
        • 2017-11-22
        相关资源
        最近更新 更多