【问题标题】:Blazor server authenticationBlazor 服务器身份验证
【发布时间】:2021-10-26 07:21:01
【问题描述】:

我想向我的 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。我做什么坏事? 非常感谢

【问题讨论】:

    标签: c# .net authentication blazor claims


    【解决方案1】:

    身份验证是您的 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 副本的第二次更新。

    首先,您需要确保在 ServerSideBlazor 之后加载新的 AuthenticationStateProvider(它会加载 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();
        }
    }
    

    【讨论】:

    • 您好,我使用 Blazor 服务器端。我厌倦了你的解决方案,但它不起作用......
    • 使用可插入并用于测试的有效“哑”设置查看更新的答案。
    • 嗨,它对我不起作用...我按照您的编写方式进行操作,而 myAuthenticationStateProvider 为空。但我注意到 .razor 中的 authenticationStateProvider 来自命名空间 Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider。但是我启动和_Imports 我有Microsoft.AspNetCore.Components.Authorization。所以我不明白这一点。谢谢你。这是截图:i.ibb.co/2gCsWXz/Untitled.png
    • 我尝试了一切,但没有任何工作......
    • 如果可以,请发布您的项目或行为相同的测试项目。暂时到 GitHub。您在项目“基础设施”中遗漏了一些东西。
    猜你喜欢
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 2021-02-27
    • 2022-12-28
    • 2021-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多