带有Blazor服务器端组件的MVC应用中的防伪令牌验证

时间:2019-12-01 01:13:30

标签: c# asp.net-core asp.net-core-mvc blazor blazor-server-side

随着ASP.Net Core 3.0的发布,我试图在我的MVC核心应用程序中利用服务器端Blazor功能。 我首先创建了一个简单的导航组件,从中试图让用户使用指向控制器动作的按钮退出。但是,我遇到了防伪令牌验证错误,内容为:

  

Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker:信息:过滤器“ Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter”上的请求授权失败。

然后服务器返回http 400状态代码。 我正在使用Blazzor App模板代码(Visual Studio 2019中的Blazor版本0.7.0):

<AuthorizeView>
    <Authorized>
        <a href="/Account/Manage">Hello, @context.User.Identity.Name!</a>
        <form method="post" action="/Account/Logout">
            <button type="submit">Sign out</button>
        </form>
    </Authorized>
    <NotAuthorized>
        <a href="/Account/Register">Register</a>
        <a href="/Account/Login">Log in</a>
    </NotAuthorized>
</AuthorizeView>

我的MVC“帐户”控制器具有以 [ValidateAntiForgeryToken] 注释的注销操作,如下所示:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
    await _signInManager.SignOutAsync();
    _logger.LogInformation("User logged out.");
    return RedirectToAction(nameof(HomeController.Index), "Home");
}

过去,我将以一种形式添加@ Html.AntiForgeryToken()以符合我带注释的Logout操作。在较新版本的ASP.Net Core中,您不再需要手动添加它,因为它已包含在表单标签帮助器中。但是,这两种方法似乎都无法翻译或在Blazor组件中可用。

您如何从Blazor组件调用此MVC动作,并提供防伪令牌进行控制器验证? 还是根本不需要这种验证?

即使我的登出案例不是很大的威胁,调用其他类似注解的操作也是一样。

我可能会缺少适用于MVC和Blazor混合场景的CSRF技术概念。

2 个答案:

答案 0 :(得分:1)

不确定为什么会弹出此异常。虽然这对我有用。 只需将其添加到_Host.cshtml文件的末尾即可。

   @Html.AntiForgeryToken()
</body>

答案 1 :(得分:1)

我一直在尝试使防伪功能适用于注销 POST。我想我只是设法让它工作。我愿意接受改进建议!

我使用了关于“Blazor 服务器附加安全方案”(https://docs.microsoft.com/en-us/aspnet/core/blazor/security/server/additional-scenarios?view=aspnetcore-5.0) 的 MS 文档,该文档解释了存储 OIDC 访问和刷新令牌以在 Blazor 组件中使用的方法。我修改了该示例,以便以类似的方式使用防伪令牌,如下所示:

  1. 我创建了一个 TokenProvider 类来存储防伪令牌。
public class TokenProvider
{
    public string AntiforgeryToken { get; set; }
}
  1. 在 Startup.ConfigureServices() 中向 DI 添加了 Scoped TokenProvider
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<TokenProvider>();
    ...
}
  1. 在 _Host.cshtml 中获取防伪令牌并将其作为参数传递给我的应用组件
@page "/"
@namespace My.Pages
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery antiforgery
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@{
    var token = antiforgery.GetAndStoreTokens(HttpContext).RequestToken;
}
...
<body>
  <component type="typeof(App)" render-mode="ServerPrerendered" param-AntiforgeryToken="token" />
...
  1. App.razor 然后在 OnInitializedAsync() 期间填充 TokenProvider
@inject TokenProvider tokenProvider

<Router ....
</Router>

@code {
    [Parameter]
    public string AntiforgeryToken { get; set; }

    protected override Task OnInitializedAsync()
    {
        tokenProvider.AntiforgeryToken = AntiforgeryToken;
        return base.OnInitializedAsync();
    }
}
  1. 然后可以通过在我的 Logout.razor 组件中注入来使用 TokenProvider
@inject TokenProvider tokenProvider
...
<form method="post" action="Logout">
    <input name="__RequestVerificationToken" type="hidden" value="@tokenProvider.AntiforgeryToken">
    <button type="submit" class="nav-link btn btn-link" title="Log out">
        <span class="oi oi-account-logout"></span>
    </button>
</form>
...