Blazor 服务器身份验证

Blazor server authentication

我想为我的 Blazor 服务器端应用程序添加身份验证。我有这个代码登录。

var claims = new List<Claim>
{
    new Claim(type: ClaimTypes.NameIdentifier, user.Username),
    new Claim(type: ClaimTypes.Name, user.Name ?? user.Username),
    new Claim(type: ClaimTypes.Sid, user.ID.ToString())
};
if (user.Email != null)
    claims.Add(new Claim(type: ClaimTypes.Email, user.Email));

if (user.UserRoles != null)
{
    foreach (var userRole in user.UserRoles)
    {
        claims.Add(new Claim(type: ClaimTypes.Role, userRole.ID_Role));
    }
}

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

var authProperties = new AuthenticationProperties
{
    AllowRefresh = _authModel.Value.AllowRefresh,
    ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(_authModel.Value.LoginExpirationMinutes),
    IsPersistent = input.IsPersistent
};

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

一切看起来都很好。代码正常运行。但是当我看到 HttpContext.User 或 AuthenticationStateProvider 时,它是空的。在资源管理器中我没有看到 cookie。我做什么不好? 非常感谢

身份验证是在您的 Blazor 应用程序内部进行的还是您正在执行任何 API 访问?

通常情况下,您会移交给 SPA 身份验证提供程序的外部人员,然后后者会使用 HttpContext.User 集回调您的 SPA。默认 AuthenticationStateProvider 然后从 HttpContext.User.

读取用户数据

您正在 SPA 内部进行身份验证。启动页面已加载并且 HttpContext.User 已设置。我不确定您是否可以重置它。你可以写一个自定义AuthenticationStateProvider。看看我对问题 Custom AuthenticationStateProvider in blazor project doesn't work on server side 的回答,其中显示了如何构建测试 AuthenticationStateProvider。您应该能够将其与您的代码一起插入。

更新

这是一个非常愚蠢的AuthenticationStateProvider,它为两个固定用户提供身份验证。从按钮调用 ChangeIdentity 进行切换。您无需尝试使用 HttpContext.

做任何事情
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Blazor.Auth.Test
{
    public class DumbAuthenticationStateProvider : AuthenticationStateProvider
    {
        public static ClaimsPrincipal User
            => new ClaimsPrincipal(new ClaimsIdentity(UserClaims, "Dumb Auth Type"));

        public static Claim[] UserClaims
            => new[]{
                    new Claim(ClaimTypes.Sid, "024672e0-250a-46fc-bd35-1902974cf9e1"),
                    new Claim(ClaimTypes.Name, "Normal User"),
                    new Claim(ClaimTypes.NameIdentifier, "Normal User"),
                    new Claim(ClaimTypes.Email, "user@user,com"),
                    new Claim(ClaimTypes.Role, "User")
            };
        public static ClaimsPrincipal Visitor
            => new ClaimsPrincipal(new ClaimsIdentity(VisitorClaims, "Dumb Auth Type"));

        public static Claim[] VisitorClaims
            => new[]{
                    new Claim(ClaimTypes.Sid, "324672e0-250a-46fc-bd35-1902974cf9e1"),
                    new Claim(ClaimTypes.Name, "Visitor"),
                    new Claim(ClaimTypes.NameIdentifier, "Normal Visitor"),
                    new Claim(ClaimTypes.Email, "visitor@user,com"),
                    new Claim(ClaimTypes.Role, "Visitor")
            };

        bool _switch;

        public override Task<AuthenticationState> GetAuthenticationStateAsync()
            => Task.FromResult(_switch 
                ? new AuthenticationState(User)
                : new AuthenticationState(Visitor)
                );

        public Task<AuthenticationState> ChangeIdentity()
        {
            _switch = !_switch;
            var task = this.GetAuthenticationStateAsync();
            this.NotifyAuthenticationStateChanged(task);
            return task;
        }

    }
}

StartUp 中的服务设置:

services.AddScoped<AuthenticationStateProvider, DumbAuthenticationStateProvider>();
services.AddAuthorizationCore();
   <button class="btn btn-dark" @onclick="ChangeID">Switch</button>

@code {

   [Inject] private AuthenticationStateProvider authenticationStateProvider {get; set;}

    private DumbAuthenticationStateProvider myAuthenticationStateProvider  => authenticationStateProvider as DumbAuthenticationStateProvider;

    private async Task ChangeID()
    {
        await myAuthenticationStateProvider.ChangeIdentity();
    }
}
 

基于 GitHub 应用程序副本的第二次更新。

首先你需要确保新的 AuthenticationStateProviderServerSideBlazor 之后加载(它加载 ServerAuthenticationSateProvider 会重载之前加载的任何内容)并清理 StartUp:

        {
            //services.AddAuthentication(
            //    CookieAuthenticationDefaults.AuthenticationScheme)
            //    .AddCookie();
            // services.AddAuthorization();

            //services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<DumbAuthenticationStateProvider>());

            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddScoped<AuthenticationStateProvider, DumbAuthenticationStateProvider>();
            services.AddAuthorizationCore();
            services.AddSingleton<WeatherForecastService>();
        }

更新 App.razor - 将 RouteView 更改为 AuthorizeRouteView

<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

我的测试页

@page "/fetchdata"
@using Microsoft.AspNetCore.Components.Authorization

@using WebApplication6.Data
<AuthorizeView>
    <Authorized>
        <div class="m-2 p-2">
            <a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
        </div>
    </Authorized>
</AuthorizeView>


<button class="btn btn-dark" @onclick="ChangeID">Switch</button>

@code {

    [Inject] private AuthenticationStateProvider authenticationStateProvider { get; set; }

    private DumbAuthenticationStateProvider myAuthenticationStateProvider => authenticationStateProvider as DumbAuthenticationStateProvider;

    private async Task ChangeID()
    {
        await myAuthenticationStateProvider.ChangeIdentity();
    }
}