如何使用 Blazor 服务器对用户进行身份验证

Cod*_*Lee 11 c# authentication mongodb asp.net-core blazor

我有一个 Blazor 服务器应用程序,它使用 MongoDB 作为数据库,所以我试图用它来实现身份验证。所以我可以<Authenticted>, <AuthorizeView Roles="admin">在剃刀页面中使用和其他类似的标签。

内置的身份验证模板使用 SQL Server,在这种情况下我不想要,并且没有一个明确的示例说明如何自己使用另一个数据库来完成。鉴于微软在此处提供的示例

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

namespace BlazorSample.Services
{
    public class CustomAuthStateProvider : AuthenticationStateProvider
    {
        public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var identity = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.Name, "mrfibuli"),
            }, "Fake authentication type");

            var user = new ClaimsPrincipal(identity);

            return Task.FromResult(new AuthenticationState(user));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

应该如何在应用程序中使用它?您显然不会将单个值和值类型硬编码为唯一的身份验证来源。那么应该如何参数化呢?具有本地属性,例如:

Username { get; set; }
UserType { get; set; }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你会在哪里设置?

另外,您将如何使用它来验证用户?我在ConfigurationServices(...)方法下的启动文件中添加了类:

...
services.AddScoped<AuthenticationStateProvider, MongoAuthenticationStateProvider>();
...
Run Code Online (Sandbox Code Playgroud)

我不知道如何验证任何人。我想你会以多种方式验证用户名和密码,然后当你知道它很好时,你继续更新 .NET 中的身份验证。我正在关注一个教程,他们在后面的代码中提出了类似的建议:

using System;
using System.Linq;
using DocsPlatform.Services;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;

namespace DocsPlatform.Pages
{
    public class LoginBase : ComponentBase
    {
        [CascadingParameter]
        private Task<AuthenticationState> authStateTask { get; set; }
        protected string username { get; set; }
        protected string password { get; set; }

        protected async Task LoginUser()
        {
            bool isValid = true;

            isValid = dbService.ValidateUser(username, password);

            string email = dbService.GetEmail(username);

            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, username),
                new Claim(ClaimTypes.Email, email),
            };

            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity));

            return NavigationManager.NavigateTo("/");
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

但是,返回中的导航不起作用(他们没有解释他们的代码甚至是如何编译的)并且 SignInAsync() 方法在他们显示的方式中不可用。同样,我不知道他们的代码是如何编译的。那么人们通常会如何做到这一点呢?

我找不到任何教程,范例等比其他数百个例子,仅仅使用内置的SQL服务器模板。在哪里可以找到有关如何使用它的详细信息?除了“使用内置模板”或此处的文档链接之外的任何内容都将不胜感激,因为它们都没有解释如何执行此操作。

itm*_*nus 16

内置的身份验证模板使用SQL Server,在这种情况下我不想要,并且没有一个明确的例子说明如何使用另一个数据库自己做

我猜你正在使用 ASP.NET Core Identity,对吧?如果您正在寻找使用其他提供程序的方式,请参阅官方文档

应该如何在应用程序中使用它?您显然不会将单个值和值类型硬编码为唯一的身份验证来源。那么应该如何参数化呢?

由于您使用的是 Blazor 服务器(而不是 Blazor Wasm),因此您不必自定义GetAuthenticationStateAsync()方法然后手动创建主体。已经有一个ServerAuthenticationStateProvider既继承自接口AuthenticationStateProvider又实现IHostEnvironmentAuthenticationStateProvider接口的内置函数:

// source code of the built-in ServerAuthenticationStateProvider 
public class ServerAuthenticationStateProvider : AuthenticationStateProvider, IHostEnvironmentAuthenticationStateProvider
{
    private Task<AuthenticationState> _authenticationStateTask;

    /// <inheritdoc />
    public override Task<AuthenticationState> GetAuthenticationStateAsync()
        => _authenticationStateTask
        ?? throw new InvalidOperationException($"{nameof(GetAuthenticationStateAsync)} was called before {nameof(SetAuthenticationState)}.");

    /// <inheritdoc />
    public void SetAuthenticationState(Task<AuthenticationState> authenticationStateTask)
    {
        _authenticationStateTask = authenticationStateTask ?? throw new ArgumentNullException(nameof(authenticationStateTask));
        NotifyAuthenticationStateChanged(_authenticationStateTask);
    }
}
Run Code Online (Sandbox Code Playgroud)

如上所示,GetAuthenticationStateAsync()将返回由 设置的身份验证状态IHostEnvironmentAuthenticationStateProvider。所以你需要的是注入一个IHostEnvironmentAuthenticationStateProvider并调用IHostEnvironmentAuthenticationStateProvider::SetAuthenticationState(...). 最后,身份验证状态将<Authorize/>自动发送到 Blazor 。

其实,以上ServerAuthenticationStateProvider都不知道本金是否仍然有效。因此,有另一种内置的具体类为您提供:RevalidatingServerAuthenticationStateProvider

以上代码适用于所有身份验证方案,包括 ASP.NET Core Identity、JwtBearer、AAD 等。您使用的身份验证方案或使用的数据库无关紧要。只是扩展RevalidatingServerAuthenticationStateProvider类。

例如,如果您使用的是ASP.NET核心身份(您可能会看到相关的Cookie的问题(见本线),它会产生一类的RevalidatingIdentityAuthenticationStateProvider使用UserManager<TUser>,以验证主体是否是有效的。

public class RevalidatingIdentityAuthenticationStateProvider<TUser>
    : RevalidatingServerAuthenticationStateProvider where TUser : class
{
    ...

    protected override async Task<bool> ValidateAuthenticationStateAsync(
        AuthenticationState authenticationState, CancellationToken cancellationToken)
    {
        // Get the user manager from a new scope to ensure it fetches fresh data
        // use the UserManager to determine whether the current principal is still valid
Run Code Online (Sandbox Code Playgroud)

由于ASP.NET Core Identity不限于 SQL Server,因此RevalidatingIdentityAuthenticationStateProvider适用于其他数据库。如果您想使用 MongoDB,请随意创建自定义MyMongoDbRevalidatingAuthenticationStateProvider.

此外,您将如何使用它来验证用户

只需像这样声明组件:

public class RevalidatingIdentityAuthenticationStateProvider<TUser>
    : RevalidatingServerAuthenticationStateProvider where TUser : class
{
    ...

    protected override async Task<bool> ValidateAuthenticationStateAsync(
        AuthenticationState authenticationState, CancellationToken cancellationToken)
    {
        // Get the user manager from a new scope to ensure it fetches fresh data
        // use the UserManager to determine whether the current principal is still valid
Run Code Online (Sandbox Code Playgroud)

你会这样做手工,如果你使用的是默认RevalidatingServerAuthenticationStateProvider。使用 Blazor 服务器端,身份验证由 完成AuthenticationMiddleware,然后身份验证状态将<AuthorizeView/>自动传递给。并且当认证状态过期时,<AuthorizeView/>也会自动更新。

返回中的导航不起作用

实际上,您的代码应该在导航之前失败:

HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity));
Run Code Online (Sandbox Code Playgroud)

请注意 SignIn() 方法将尝试通过 HTTP 发送 cookie。但是,大多数情况下,建立连接后没有 HTTP。其实几个月前我已经回答了一个完全相同的问题


简而言之:

  1. 您使用哪种身份验证方案无关紧要,如果需要,请实施RevalidatingServerAuthenticationStateProvider类似的方案RevalidatingIdentityAuthenticationStateProvider
  2. 如果您使用的是 ASP.NET Core Identity,则会RevalidatingIdentityAuthenticationStateProvider为您生成一个。
  3. 如果你想使用 ASP.NET Core Identity + MongoDB,请按照官方文档来实现这样的功能。
  4. 如果要使用 ASP.NET Core Identity + Blazor Server Side,并使用 SignIn(...) 发送 cookie,请不要直接执行此操作。有关更多详细信息,请参阅此线程

  • 您有关于如何实现您提到的类的任何项目或示例代码吗? (2认同)