我们使用 Blazor 服务器端作为在 Azure Service Fabric 中运行的一组微服务(本身是无状态的 ASP.NET Core 服务)的前端,因此这是一个完全合法的场景。我们的整个应用程序在 Azure 的云中运行,所以我将使用它的产品来描述我们的实现。
为了更好地分离关注点并更轻松地保护互联网边界,我们为公共 API 和 SignalR 提供了两个独立的服务。 API 网关因此充当安全边界,因为它可以处理 HTTPS 卸载、位于面向公众的子网上并路由到虚拟网络上的内部服务。然后,提供 SignalR 访问的服务在应用程序网关实例后面的面向公众的子网上运行(用于防火墙 + 服务路由功能)。
因此,我们将两个服务设置如下:
SignalR 服务
[浏览器] -- (SignalR) -- [Azure 应用程序网关] -- [Azure Service Fabric - Blazor 服务器端应用程序] -- [SF 服务远程处理] -- [微服务]
API 服务
[浏览器] -- (https) -- [Azure API 管理] -- (http) -- [Azure Service Fabric - ASP.NET Core API] -- [SF Service Remoting] -- [微服务]
微服务陷阱 - 数据保护
特别是在支持身份验证时,ASP.NET Core 在内部设置了一些密钥,用于加密 Blazor 会话中使用的状态。在微服务场景中,除非您的网络堆栈(负载均衡器、网关、路由器等)一直有粘性路由,以确保所有请求始终命中同一个实例(并且可能不会重建您的实例并在您不注意时转移到其他地方),您将遇到关于未能取消保护状态的模糊且无益的错误。
修复非常简单 - 在 ASP.NET Core 服务的 Startup.cs 中,确保设置 services.AddDataProtection(); 并进行适当配置。由于我们使用的是 Service Fabric,因此我们使用了一个 third-party library,它非常适合此目的。要使用,请将 NuGet 包 SoCreate.AspNetCore.DatapProtection.ServiceFabric 安装到您的 ASP.NET Core 服务中,并将以下内容放入您的 Startup.cs 中的 ConfigureServices() 方法中:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddDataProtection().PersistKeysToServiceFabricDistributedCache(opt => {
//Unique for this ASP.NET Core microservice, don't use a new GUID each time or you'll be back where you started without a single store for all the services
opt.CacheStoreId = new Guid("b87d03b5-f8d1-456f-966d-11d2c4d9774d");
//Specifically points to the service to use - if not set, it'll be automatically discovered
opt.CacheStoreServiceUri = new Uri("fabric:/MyApplication/DataProtectionStore"); });
//...
}
您可以在后一种方法上指定其他选项来标识它应该使用的特定服务和唯一 ID(因此服务可以在应用程序之间共享),但如果它在同一个应用程序中并且您没有指定这些选项,它会自己找到。
然后创建一个有状态服务实例,在反映服务名称的文件中从 NuGet 安装 SoCreate.Extensions.Caching.ServiceFabric,并将其从 StatefulService 继承替换为从 DistributedCacheStoreService 继承。删除服务中的所有其他内容,但设置更新的构造函数。在名为“DataProtectionStore”的服务中,您的 DataProtectionStore.cs 文件应如下所示:
using System.Fabric;
using SoCreate.Extensions.Caching.ServiceFabric
namespace DataProtectionStore
{
internal sealed class DataProtectionStore : DistributedCacheStoreService
{
protected override int MaxCacheSizeInMegabytes => 500; //Optional
public DataProtectionStore(StatefulServiceContext context) : base(context, message => ServiceEventSource.Current.ServiceMessage(context, message))
{
}
}
}
您可以找到其他有关在“网络场”或微服务/分布式环境中托管 ASP.NET Core 的有用指南here。