【问题标题】:.NET 5 Blazor ServerSide AND WebAssembly (hybrid) with WebAPI Authentication?.NET 5 Blazor 服务器端和带有 Web API 身份验证的 WebAssembly(混合)?
【发布时间】:2021-06-04 10:35:33
【问题描述】:

有没有办法以混合方式将默认身份验证 (.NET 5 / Identity-Server) 与 Blazor ServerSide Blazor WebAssembly 结合使用? 我想要一个 Blazor 项目,它可以在 ClientSide (WebAssembly) 和 ServerSide 之间切换,以保持 Client 相同,我想在 ClientSide ServerSide上使用 WebAPI。我从 ServerSide 开始(更好的调试和更好的表性能 *),以后可能会切换到 ClientSide(如果性能会更好)。

*请不要开始讨论什么更好,或者如果您可能在 WebAssembly 上有良好的性能,那么您没有很多嵌套组件,例如基于组件的表。

我确实测试了这么多组合,但无法让它发挥作用,但让我们从基础开始:

  • 创建 Blazor WebAssembly 项目(托管 ASP.Net Core)
  • 将 .NET 5 与身份验证(帐户)结合使用
  • 编辑服务器 startup.cs(如下所示)
  • 编辑客户端 program.cs(如下所示)
  • 将“_Host.cshtml”添加到“\Server\Pages”(如下所示)

Startup.cs(服务器)

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddIdentityServer().AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    services.AddAuthentication().AddIdentityServerJwt();
    
    // SERVER SIDE SUPPORT
    services.AddServerSideBlazor();
    services.AddApiAuthorization();
    if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
    {
        services.AddScoped(s =>
        {
            var uriHelper = s.GetRequiredService<NavigationManager>();
            return new HttpClient
            {
                BaseAddress = new System.Uri(uriHelper.BaseUri)
            };
        });
    }
    // --

    services.AddControllersWithViews();
    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
        app.UseWebAssemblyDebugging();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseIdentityServer();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllers();
        
        // SERVER-SIDE SUPPORT
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host"); //endpoints.MapFallbackToFile("index.html");
        // --
    });
}

Program.cs(客户端)

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    //builder.RootComponents.Add<App>("#app");

    builder.Services.AddHttpClient("BlazorSwitchWithSec2.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
        .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

    // Supply HttpClient instances that include access tokens when making requests to the server project
    builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorSwitchWithSec2.ServerAPI"));

    builder.Services.AddApiAuthorization();

    await builder.Build().RunAsync();
}

_Host.cshtml(服务器)

@page "/"
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        @if (Request.QueryString.Value.ToLower().Contains("mode=client"))
        {
            <title>WebApp (CE)</title>
        }
        else
        {
            <title>WebApp (SE)</title>
        }
        <base href="~/" />
        <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
        <link href="css/app.css" rel="stylesheet" />
    </head>

    <body>
        @if (Request.QueryString.Value.ToLower().Contains("mode=client"))
        {
            <component type="typeof(BlazorSwitchWithSec2.Client.App)" render-mode="WebAssemblyPrerendered" />
            <script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
            <script src="_framework/blazor.webassembly.js"></script>
            <script>navigator.serviceWorker.register('service-worker.js');</script>
        }
        else
        {
            <component type="typeof(BlazorSwitchWithSec2.Client.App)" render-mode="Server" />
            <script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
            <script src="_framework/blazor.server.js"></script>
        }
    </body>
</html>

如果我尝试通过 WebAssembly 或服务器注册或登录,我会收到以下错误: 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]'

也许这也有帮助:我尝试了一些不同的方法,比如使用来自客户端的服务,一种组合似乎可以与没有 [Authorize] 属性的控制器一起使用,但是有了它,我得到了一个类似 Unable to parse ... 的错误 - 结果是登录网址(这就是解析不起作用的原因)。我不能发布代码,因为我测试了太多,项目不再工作了。

没有任何身份验证一切正常,我可以向 WebAPI 发送一个令牌来创建我自己的登录系统。但是我想试试默认的。

【问题讨论】:

  • 虽然你对它们进行相同的编程,但是服务器端和 wasm 是完全不同的。由于 wasm 在客户端浏览器中天真地运行,因此需要一种不同的身份验证方法。
  • 我知道,默认代码在客户端工作正常(如果我不更改任何内容)。服务器端“只是”缺少一个部分来告诉“使用您的 cookie / 存储并将您的数据发送到 api”。我的意思是默认的服务器端代码也可以工作(使用身份验证),但没有 API 访问权限。而且它们之间的(代码)差异似乎很小。

标签: c# authentication blazor-server-side blazor-webassembly


【解决方案1】:

这些来自 Carl Franklin 的 Blazor Train 视频可能会对您有所帮助。

Blazor 同步性;同时开发服务器和 WASM 应用

https://www.youtube.com/watch?v=SkYQDPXw__c

Blazor 同步 5.0

https://www.youtube.com/watch?v=fHzIWOfmqzg

【讨论】:

  • 我做了将近 20 次这种设置,但你仍然有 Authentication 的问题,因为在服务器端你有 SignalR,而在 Wasm 你有 Http。到目前为止,唯一可行的方法是使用自己的登录系统(在每个请求上发送一个令牌)。
  • 我明白了,出于兴趣,您现在为什么不专注于服务器端,因为您知道当您使用 API 时,如果它证明是有利的,切换到客户端不会很困难将来会这样做吗?
  • 这就是我目前正在做的事情?。但现在我需要上传文件,据我所知,这只能通过 Http / Web-API 实现,所以我需要一些解决方法。
  • 有可能通过 Blazor Train 的链接回答每个问题,但我想知道第 37 集中是否有任何帮助? blazortrain.com注意,WASM和Server-Side的源码都可以下载
【解决方案2】:

我不喜欢 BlazorServer 引用 BlazorWasm 的方法。我更喜欢让 BlazorServer 和 BlazorWasm 项目都引用包含应用程序的 dll(rdl),这使得两者都使用共享代码库更加清晰,并且避免了 BlazorServer 从 BlazorWasm 中引入不必要的依赖项。 你可以检查这个链接 https://github.com/mostafaefcih/BlazorDaulMode 或查看 plaursight 上的 blazor 入门课程

【讨论】:

  • 我尝试过类似你的方法,这是一种非常干净的方法。但是没有弄清楚如何在两个项目之间共享诸如 CSS 文件之类的 www 东西。现在回去为时已晚 ;) 我现在有一个使用自定义身份验证运行的单个实例,但我可以在运行时在 Server 和 Wasm 之间切换。
  • @StewieG 你可以将它添加到每个项目的根文件夹中(Wasm->index.html 或 blzaor 服务器中的 host.cshtml)
猜你喜欢
  • 2011-11-04
  • 1970-01-01
  • 2023-01-30
  • 2020-11-02
  • 1970-01-01
  • 1970-01-01
  • 2020-09-16
  • 1970-01-01
  • 2021-04-30
相关资源
最近更新 更多