防止直接 url 访问 razor 页面处理程序
Prevent direct url access to a razor page handler
我制作了 razor 页面来注销用户。当经过身份验证的用户访问路由 /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?
Se this question 可能的实现方式。
不确定我是否推荐走那条路,因为它有点脏而且是一个不必要的复杂解决方案。您可以 return 为登录的 in/out 用户显示不同的状态消息。
Logout.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();
}
}
}
Logout.cshtml
@page
@model X.LogoutModel
@Model.StatusMessage
我已经使用 Attribute 和 IPageFilter 解决了这个问题,它检查了 Referer header。
首先我创建了属性
[AttributeUsage(AttributeTargets.Method)]
public class ChildHandlerAttribute : Attribute
{
}
然后我用它装饰了 GetOnSuccess()
处理程序
[ChildHandler]
public void OnGetSuccess()
{
}
然后我实现了检查 Referer header 以获得 child 处理程序的过滤器。
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>();
});
在您的场景中,用户退出并重定向为匿名用户。所以不能说这里只有某些用户可以访问此操作。
你应该用其他方法解决这个问题。例如,您可以在 LogOut
操作中设置 cookie 中的值,然后在 Success
操作中签入。为了获得更高的安全性,请应用 MVC 用于 CSRF.
的模式
我制作了 razor 页面来注销用户。当经过身份验证的用户访问路由 /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?
Se this question 可能的实现方式。
不确定我是否推荐走那条路,因为它有点脏而且是一个不必要的复杂解决方案。您可以 return 为登录的 in/out 用户显示不同的状态消息。
Logout.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();
}
}
}
Logout.cshtml
@page
@model X.LogoutModel
@Model.StatusMessage
我已经使用 Attribute 和 IPageFilter 解决了这个问题,它检查了 Referer header。
首先我创建了属性
[AttributeUsage(AttributeTargets.Method)]
public class ChildHandlerAttribute : Attribute
{
}
然后我用它装饰了 GetOnSuccess()
处理程序
[ChildHandler]
public void OnGetSuccess()
{
}
然后我实现了检查 Referer header 以获得 child 处理程序的过滤器。
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>();
});
在您的场景中,用户退出并重定向为匿名用户。所以不能说这里只有某些用户可以访问此操作。
你应该用其他方法解决这个问题。例如,您可以在 LogOut
操作中设置 cookie 中的值,然后在 Success
操作中签入。为了获得更高的安全性,请应用 MVC 用于 CSRF.