【问题标题】:How to authenticate a user with Blazor Server如何使用 Blazor Server 对用户进行身份验证
【发布时间】:2020-06-08 12:52:53
【问题描述】:

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

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

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

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

Username { get; set; }
UserType { get; set; }

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

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

...
services.AddScoped<AuthenticationStateProvider, MongoAuthenticationStateProvider>();
...

我不知道如何验证任何人。我想您可以通过多种方式验证用户名和密码,然后当您知道这很好时,您可以继续更新 .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("/");
        }
    }
}

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

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

【问题讨论】:

  • 完美的问题说明了这是多么糟糕的记录。我和你在同一个地方。

标签: c# mongodb authentication asp.net-core blazor


【解决方案1】:

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

我猜你使用的是 ASP.NET Core Identity,对吧?如果您正在寻找使用其他提供商的方式,请参阅official docs

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

由于您使用的是 Blazor Server(而不是 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);
    }
}

正如您在上面看到的,GetAuthenticationStateAsync() 将返回由IHostEnvironmentAuthenticationStateProvider 设置的身份验证状态。所以你需要注入一个IHostEnvironmentAuthenticationStateProvider 并调用IHostEnvironmentAuthenticationStateProvider::SetAuthenticationState(...)。最后身份验证状态会自动发送到 Blazor &lt;Authorize/&gt;

其实上面的ServerAuthenticationStateProvider不知道principal是否还有效。所以还有另一个内置的具体类:RevalidatingServerAuthenticationStateProvider

以上代码适用于每种身份验证方案,包括 ASP.NET Core Identity、JwtBearer、AAD 等。您使用什么身份验证方案或使用哪个数据库都没有关系。只需扩展 RevalidatingServerAuthenticationStateProvider 类即可。

例如,如果您使用的是 ASP.NET Core Identity(您可能会看到与 Cookies 相关的问题(请参阅 this thread),它将生成一个 RevalidatingIdentityAuthenticationStateProvider 类使用UserManager&lt;TUser&gt;来验证principal是否有效。

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

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

然后你将如何使用它来验证用户

只需像这样声明组件:

<AuthorizeView>
    <Authorized>
        ...
    </Authorized>
    <NotAuthorized>
       ...
    </NotAuthorized>
</AuthorizeView>

如果您使用默认的RevalidatingServerAuthenticationStateProvider,您将手动执行此操作。在 Blazor Server Side 中,身份验证由 AuthenticationMiddleware 完成,然后身份验证状态将自动传递给 &lt;AuthorizeView/&gt;。并且当认证状态过期时,&lt;AuthorizeView/&gt;也会自动更新。

返回中的导航不起作用

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

HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity));

请注意,SignIn() 方法将尝试通过 HTTP 发送 cookie。但是,大多数情况下,连接建立后没有 HTTP。其实我有answered an exactly same question several month ago


简而言之:

  1. 无论您使用哪种身份验证方案,都可以根据需要实现RevalidatingServerAuthenticationStateProvider,如RevalidatingIdentityAuthenticationStateProvider
  2. 如果您使用的是 ASP.NET Core Identity,则会为您生成一个 RevalidatingIdentityAuthenticationStateProvider
  3. 如果您想使用 ASP.NET Core Identity + MongoDB,请关注official docs 实现这样的功能。
  4. 如果要使用 ASP.NET Core Identity + Blazor Server Side,并使用 SignIn(...) 发送 cookie,请不要直接这样做。有关详细信息,请参阅this thread

【讨论】:

  • 你有关于如何实现你提到的类的项目或示例代码吗?
猜你喜欢
  • 1970-01-01
  • 2021-09-20
  • 2015-04-03
  • 2021-07-26
  • 1970-01-01
  • 2019-05-02
  • 2013-01-19
  • 2018-12-08
  • 2012-08-26
相关资源
最近更新 更多