首页 > 解决方案 > 托管在 ASP.NET Core 项目上的 Blazor Webassembly 项目中的身份验证问题

问题描述

我有两个项目:

Notes.Server- ASP.NET Core 项目,
Notes.Client- Blazor WASM 项目

我的笔记应用程序包含Client.cshtml具有以下内容的页面:

<component type="typeof(PrettyNotes.Web.Client.App)" render-mode="WebAssemblyPrerendered"/>

它预装了 Blazor WASM。我在这个预渲染项目中实现了 oidc 身份验证。

Notes.Server 的Startup.cs文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using PrettyNotes.Web.Server.Models;
using PrettyNotes.Web.Server.Models.Data;

namespace Notes.Web.Server
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddRazorPages().AddRazorRuntimeCompilation();
            services.AddDbContext<PrettyNotesApplicationDBContext>(options =>
            {
                options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=ProjectDB;Trusted_Connection=True;");
            });

            services.AddIdentity<PNUser, IdentityRole>(options =>
            {
                options.Password.RequiredLength = 8;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireDigit = true;
            }).AddEntityFrameworkStores<PrettyNotesApplicationDBContext>();
            services.AddAuthentication().AddIdentityServerJwt();

            services.PostConfigure<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme,
                opt =>
                {
                    opt.LoginPath = "/auth/login";
                    opt.LogoutPath = "/auth/logout";
                });

            services.AddIdentityServer()
                .AddInMemoryApiResources(new ApiResource[] { new ApiResource("API", "ServerAPI") }).AddInMemoryClients(
                    new[]
                    {
                        new IdentityServer4.Models.Client
                        {
                            ClientId = "PrettyNotesClient",
                            AllowedGrantTypes = GrantTypes.Code,
                            RequirePkce = true,
                            RequireClientSecret = false,
                            AllowedCorsOrigins = { "https://localhost:5001" },
                            AllowedScopes = { "openid", "profile", "email" },
                            RedirectUris = { "https://localhost:5001/client/security/auth/login-callback" },
                            PostLogoutRedirectUris = { "http://localhost:5001/client" },
                            Enabled = true
                        }
                    })
                .AddInMemoryIdentityResources(
                    new IdentityResource[]
                    {
                        new IdentityResources.Address(),
                        new IdentityResources.Profile(),
                        new IdentityResources.Email(),
                        new IdentityResources.OpenId()
                    }).AddInMemoryApiScopes(new[] { new ApiScope("API") }).AddAspNetIdentity<PNUser>()
                .AddDeveloperSigningCredential();
            services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>().AddScoped<SignOutSessionStateManager>();
            services.AddRemoteAuthentication<RemoteAuthenticationState, RemoteUserAccount, OidcProviderOptions>();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();
            app.UseStaticFiles();
            app.UseBlazorFrameworkFiles();

            app.UseAuthentication();
            app.UseAuthorization();
            app.UseIdentityServer();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapRazorPages();
                endpoints.MapFallbackToController("/client/{**segment}", "Index", "Client");
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

我的 Blazor WASM 项目在服务器中预渲染。当我尝试访问预渲染 Blazor 的 Blazor WASM 项目页面时,出现下一个错误:

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.InvalidCastException: Unable to cast object of type 'Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider' to type 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.IRemoteAuthenticationService`1[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState]'.

如何解决这个问题?您能否推荐一些关于 Prerendered Blazor Webassembly 中的身份验证的资源和存储库?

标签: c#asp.net-coreasp.net-identityidentityserver4blazor-webassembly

解决方案


这里的文档应该已经有很大帮助了:https ://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-6.0#support-prerendering-with-authentication

尽管他们似乎忽略了回调登录 url 停留在“加载”上的事实,因为您需要禁用 div 覆盖以进行预渲染,但仍然在弄清楚这一点


推荐阅读