• ABP微服务系列学习-搭建自己的微服务结构(三)


    上一篇我们基础服务初步搭建完毕,接下来我们整一下认证和网关。

    搭建认证服务#

    认证服务的话,ABP CLI生成的所有模板都包括了一个AuthServer。我们直接生成模板然后微调一下就可以直接用了。

    abp new FunShow -t app --tiered
    

    使用命令创建模板后,我们可以找到一个AuthServer。把项目移动到Apps目录下,然后我们开始改造一下这个项目。
    首先修改项目文件的引用配置
    修改EFCore项目引用为AdministrationService.EntityFrameworkCore和IdentityService.EntityFrameworkCore,
    然后添加Shared.Localization和Shared.Hosting.AspNetCore项目引用,别的基本不用怎么修改,完整项目配置为:

    <Project Sdk="Microsoft.NET.Sdk.Web">
    
      <Import Project="..\..\..\..\common.props" />
    
      <PropertyGroup>
        <TargetFramework>net7.0TargetFramework>
        <UserSecretsId>b83bc18b-a6ca-4e2d-a827-26ffaff35dceUserSecretsId>
        <DockerDefaultTargetOS>LinuxDockerDefaultTargetOS>
        <DockerfileContext>..\..\..\..DockerfileContext>
      PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.5" />
        <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
      ItemGroup>
    
      <ItemGroup>
        <PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="7.0.0" />
        <PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="7.0.0" />
        <PackageReference Include="Volo.Abp.BackgroundJobs.RabbitMQ" Version="7.0.0" />
        <PackageReference Include="Volo.Abp.Account.Web.OpenIddict" Version="7.0.0" />
        <PackageReference Include="Volo.Abp.Account.Application" Version="7.0.0" />
        <PackageReference Include="Volo.Abp.Account.HttpApi" Version="7.0.0" />
      ItemGroup>
    
      <ItemGroup>
        <ProjectReference Include="..\..\..\..\services\administration\src\FunShow.AdministrationService.EntityFrameworkCore\FunShow.AdministrationService.EntityFrameworkCore.csproj" />
        <ProjectReference Include="..\..\..\..\services\identity\src\FunShow.IdentityService.EntityFrameworkCore\FunShow.IdentityService.EntityFrameworkCore.csproj" />
        <ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Hosting.AspNetCore\FunShow.Shared.Hosting.AspNetCore.csproj" />
        <ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Localization\FunShow.Shared.Localization.csproj" />
      ItemGroup>
    
      <ItemGroup>
        <PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.0.0-*" />
      ItemGroup>
    
      <ItemGroup>
        <Compile Remove="Logs\**" />
        <Content Remove="Logs\**" />
        <EmbeddedResource Remove="Logs\**" />
        <None Remove="Logs\**" />
      ItemGroup>
    
    Project>
    
    

    然后修改Program文件,主要是日志配置修改一下,别的不用改动

    using System;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using FunShow.Shared.Hosting.AspNetCore;
    using Serilog;
    
    namespace FunShow.AuthServer;
    
    public class Program
    {
        public async static Task<int> Main(string[] args)
        {
            var assemblyName = typeof(Program).Assembly.GetName().Name;
    
            SerilogConfigurationHelper.Configure(assemblyName);
    
            try
            {
                Log.Information($"Starting {assemblyName}.");
                var builder = WebApplication.CreateBuilder(args);
                builder.Host
                    .AddAppSettingsSecretsJson()
                    .UseAutofac()
                    .UseSerilog();
                await builder.AddApplicationAsync();
                var app = builder.Build();
                await app.InitializeApplicationAsync();
                await app.RunAsync();
                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }
    }
    
    

    修改module.cs

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography.X509Certificates;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Cors;
    using Microsoft.AspNetCore.DataProtection;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection.Extensions;
    using Microsoft.Extensions.Hosting;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using FunShow.AdministrationService.EntityFrameworkCore;
    using FunShow.IdentityService.EntityFrameworkCore;
    using FunShow.Shared.Hosting.AspNetCore;
    using Prometheus;
    using StackExchange.Redis;
    using Volo.Abp;
    using Volo.Abp.Account;
    using Volo.Abp.Account.Web;
    using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
    using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
    using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
    using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
    using Volo.Abp.Auditing;
    using Volo.Abp.BackgroundJobs.RabbitMQ;
    using Volo.Abp.Caching;
    using Volo.Abp.Caching.StackExchangeRedis;
    using Volo.Abp.Emailing;
    using Volo.Abp.EventBus.RabbitMq;
    using Volo.Abp.Modularity;
    using Volo.Abp.MultiTenancy;
    using Volo.Abp.OpenIddict;
    using Volo.Abp.UI.Navigation.Urls;
    using Volo.Abp.VirtualFileSystem;
    using Microsoft.AspNetCore.HttpOverrides;
    using FunShow.Shared.Localization;
    
    namespace FunShow.AuthServer;
    
    [DependsOn(
        typeof(AbpCachingStackExchangeRedisModule),
        typeof(AbpEventBusRabbitMqModule),
        typeof(AbpBackgroundJobsRabbitMqModule),
        typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
        typeof(AbpAccountWebOpenIddictModule),
        typeof(AbpAccountApplicationModule),
        typeof(AbpAccountHttpApiModule),
        typeof(AdministrationServiceEntityFrameworkCoreModule),
        typeof(IdentityServiceEntityFrameworkCoreModule),
        typeof(FunShowSharedHostingAspNetCoreModule),
        typeof(FunShowSharedLocalizationModule)
    )]
    public class FunShowAuthServerModule : AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();
        
        PreConfigure(builder =>
        {
        	builder.AddValidation(options =>
        {
        options.AddAudiences("AccountService");
        options.UseLocalServer();
        options.UseAspNetCore();
        });
        });
        if (!hostingEnvironment.IsDevelopment())
        {
        	PreConfigure(options =>
        	{
        	options.AddDevelopmentEncryptionAndSigningCertificate = false;
        	});
        
            PreConfigure(builder =>
            {
                builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
                builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
                });
            }
        }
        
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
        //You can disable this setting in production to avoid any potential security risks.
            Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
            
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();
            
            ConfigureBundles();
            ConfigureSwagger(context, configuration);
            ConfigureSameSiteCookiePolicy(context);
            ConfigureExternalProviders(context);
            
            Configure(options =>
            {
            	options.IsEnabled = true;
            });
            
            Configure(options =>
            {
                options.ApplicationName = "AuthServer";
            });
            
            Configure(options =>
            {
                options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
                options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(','));
            });
            
            Configure(options =>
            {
            	options.KeyPrefix = "FunShow:";
            });
            
            var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("FunShow");
            var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
            dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "FunShow-Protection-Keys");
            
            context.Services.AddCors(options =>
            {
            	options.AddDefaultPolicy(builder =>
                {
                builder
                .WithOrigins(
                configuration["App:CorsOrigins"]
                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                .Select(o => o.Trim().RemovePostFix("/"))
                .ToArray()
                )
                .WithAbpExposedHeaders()
                .SetIsOriginAllowedToAllowWildcardSubdomains()
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials();
                });
            });
            
            #if DEBUG
            context.Services.Replace(ServiceDescriptor.Singleton());
            #endif
            
            if (hostingEnvironment.IsDevelopment())
            {
                Configure(options =>
                {
                options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(
                hostingEnvironment.ContentRootPath,
                $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}shared{Path.DirectorySeparatorChar}FunShow.Shared.Localization"));
                });
            }
        }
        
        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();
            
            var configuration = context.ServiceProvider.GetRequiredService();
            
            if (env.IsDevelopment())
            {
            	app.UseDeveloperExceptionPage();
            }
        
            app.UseAbpRequestLocalization();
            
            if (!env.IsDevelopment())
            {
            	app.UseErrorPage();
            }
            var forwardOptions = new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
                RequireHeaderSymmetry = false
            };
        
            forwardOptions.KnownNetworks.Clear();
            forwardOptions.KnownProxies.Clear();
            
        // ref: https://github.com/aspnet/Docs/issues/2384
            app.UseForwardedHeaders(forwardOptions);
            
            app.UseCorrelationId();
            app.UseAbpSecurityHeaders();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseCors();
            app.UseCookiePolicy();
            app.UseHttpMetrics();
            app.UseAuthentication();
            app.UseAbpOpenIddictValidation();
            app.UseAbpSerilogEnrichers();
            app.UseUnitOfWork();
            app.UseAuthorization();
            app.UseSwagger();
            app.UseAbpSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "Account Service API");
                options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
            });
            	app.UseAuditing();
            	app.UseConfiguredEndpoints(endpoints =>
                {
                	endpoints.MapMetrics();
                });
        }
        
        private void ConfigureBundles()
        {
            Configure(options =>
            {
                options.StyleBundles.Configure(
                LeptonXLiteThemeBundles.Styles.Global,
                bundle =>
                {
                	bundle.AddFiles("/global-styles.css");
                }
            );
            });
        }
        
        private void ConfigureExternalProviders(ServiceConfigurationContext context)
        {
        	context.Services.AddAuthentication();
        }
        
        private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
        {
            var fileName = "authserver.pfx";
            var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
            var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
            
            if (!File.Exists(file))
            {
            	throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
            }
        
        	return new X509Certificate2(file, passPhrase);
        }
        
        private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
        {
            SwaggerConfigurationHelper.ConfigureWithAuth(
            context: context,
            authority: configuration["AuthServer:Authority"],
            scopes: new Dictionary<string, string> {
            /* Requested scopes for authorization code request and descriptions for swagger UI only */
            { "AccountService", "Account Service API" }
            },
            apiTitle: "Account Service API"
            );
        }
        
        private void ConfigureSameSiteCookiePolicy(ServiceConfigurationContext context)
        {
        	context.Services.AddSameSiteCookiePolicy();
        }
    }
    
    

    最后修改配置文件

    {
      "App": {
        "SelfUrl": "https://localhost:44322",
        "CorsOrigins": "http://localhost:4200,http://localhost:9527,https://localhost:44307,https://localhost:44325,https://localhost:44353,https://localhost:44367,https://localhost:44388,https://localhost:44381,https://localhost:44361",
        "RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307,https://localhost:44321,http://localhost:9527"
      },
      "AuthServer": {
        "Authority": "https://localhost:44322",
        "RequireHttpsMetadata": "true",
        "SwaggerClientId": "WebGateway_Swagger"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*",
      "ConnectionStrings": {
        "AdministrationService": "Host=localhost;Port=5432;User ID=postgres;password=myPassw0rd;Pooling=true;Database=FunShow_Administration;",
        "IdentityService": "Host=localhost;Port=5432;User ID=postgres;password=myPassw0rd;Pooling=true;Database=FunShow_Identity;"
      },
      "StringEncryption": {
        "DefaultPassPhrase": "fCrJICTG3WoyissG"
      },
      "Redis": {
        "Configuration": "localhost:6379"
      },
      "RabbitMQ": {
        "Connections": {
          "Default": {
            "HostName": "localhost"
          }
        },
        "EventBus": {
          "ClientName": "FunShow_AuthServer",
          "ExchangeName": "FunShow"
        }
      },
      "ElasticSearch": {
        "Url": "http://localhost:9200"
      }
    }
    
    

    这样我们认证服务即修改完成。

    搭建网关服务#

    网关服务我们直接新建一个空asp.net core项目。
    然后只需要添加一个我们的Shared.Hosting.Gateways项目引用即可。

    <Project Sdk="Microsoft.NET.Sdk.Web">
    
      <Import Project="..\..\..\..\common.props" />
    
      <PropertyGroup>
        <TargetFramework>net7.0TargetFramework>
      PropertyGroup>
    
      <ItemGroup>
        <ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Hosting.Gateways\FunShow.Shared.Hosting.Gateways.csproj" />
      ItemGroup>
    
      <ItemGroup>
        <Compile Remove="Logs\**" />
        <Content Remove="Logs\**" />
        <EmbeddedResource Remove="Logs\**" />
        <None Remove="Logs\**" />
      ItemGroup>
    
    Project>
    
    

    修改Program.cs

    using System;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using FunShow.Shared.Hosting.AspNetCore;
    using Serilog;
    
    namespace FunShow.WebGateway;
    
    public class Program
    {
        public async static Task<int> Main(string[] args)
        {
            var assemblyName = typeof(Program).Assembly.GetName().Name;
    
            SerilogConfigurationHelper.Configure(assemblyName);
    
            try
            {
                Log.Information($"Starting {assemblyName}.");
                var builder = WebApplication.CreateBuilder(args);
                builder.Host
                    .AddAppSettingsSecretsJson()
                    .AddYarpJson()
                    .UseAutofac()
                    .UseSerilog();
                await builder.AddApplicationAsync();
                var app = builder.Build();
                await app.InitializeApplicationAsync();
                await app.RunAsync();
                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }
    }
    
    

    这里和认证服务基本一致,就是多了一个AddYarpJson()来添加我们的yarp的配置文件。
    在目录下新建yarp.json文件,添加我们的yarp配置内容。配置集群和路由如下:

    {
      "ReverseProxy": {
        "Routes": {
          "Account Service": {
            "ClusterId": "accountCluster",
            "Match": {
              "Path": "/api/account/{**everything}"
            }
          },
          "Identity Service": {
            "ClusterId": "identityCluster",
            "Match": {
              "Path": "/api/identity/{**everything}"
            }
          },
          "Administration Service": {
            "ClusterId": "administrationCluster",
            "Match": {
              "Path": "/api/abp/{**everything}"
            }
          },
          "Logging Service": {
            "ClusterId": "loggingCluster",
            "Match": {
              "Path": "/api/LoggingService/{**everything}"
            }
          },
          "feature-management-route": {
            "ClusterId": "feature-management-cluster",
            "Match": {
              "Path": "/api/feature-management/{**everything}"
            }
          },
          "permission-management-route": {
            "ClusterId": "permission-management-cluster",
            "Match": {
              "Path": "/api/permission-management/{**everything}"
            }
          },
          "setting-management-route": {
            "ClusterId": "setting-management-cluster",
            "Match": {
              "Path": "/api/setting-management/{**everything}"
            }
          }
        },
        "Clusters": {
          "accountCluster": {
            "Destinations": {
              "destination1": {
                "Address": "https://localhost:44322"
              }
            }
          },
          "identityCluster": {
            "Destinations": {
              "destination1": {
                "Address": "https://localhost:44388"
              }
            }
          },
          "administrationCluster": {
            "Destinations": {
              "destination1": {
                "Address": "https://localhost:44367"
              }
            }
          },
          "loggingCluster": {
            "Destinations": {
              "destination1": {
                "Address": "https://localhost:45124"
              }
            }
          },
          "feature-management-cluster": {
            "Destinations": {
              "destination1": {
                "Address": "https://localhost:44367"
              }
            }
          },
          "permission-management-cluster": {
            "Destinations": {
              "destination1": {
                "Address": "https://localhost:44367"
              }
            }
          },
          "setting-management-cluster": {
            "Destinations": {
              "destination1": {
                "Address": "https://localhost:44367"
              }
            }
          }
        }
      }
    }
    

    在appsettings.json文件添加我们认证服务的地址

    {
      "App": {
        "SelfUrl": "https://localhost:44325",
        "CorsOrigins": "http://localhost:4200,https://localhost:44307,http://localhost:9527"
      },
      "AuthServer": {
        "Authority": "https://localhost:44322",
        "RequireHttpsMetadata": "true",
        "SwaggerClientId": "WebGateway_Swagger"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*",
      "Redis": {
        "Configuration": "localhost:6379"
      },
      "ElasticSearch": {
        "Url": "http://localhost:9200"
      }
    }
    
    

    最后我们添加FunShowWebGatewayModule文件。配置我们yarp的服务。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Cors;
    using Microsoft.AspNetCore.Rewrite;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using FunShow.Shared.Hosting.AspNetCore;
    using FunShow.Shared.Hosting.Gateways;
    using Volo.Abp;
    using Volo.Abp.Modularity;
    
    namespace FunShow.WebGateway;
    
    [DependsOn(
        typeof(FunShowSharedHostingGatewaysModule)
    )]
    public class FunShowWebGatewayModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            // Enable if you need hosting environment
            // var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            
            SwaggerConfigurationHelper.ConfigureWithAuth(
                context: context,
                authority: configuration["AuthServer:Authority"],
                scopes: new
                Dictionary<string, string> /* Requested scopes for authorization code request and descriptions for swagger UI only */ {
                { "AccountService", "Account Service API" },
                { "IdentityService", "Identity Service API" },
                { "AdministrationService", "Administration Service API" },
                { "LoggingService", "Logging Service API" }
                },
                apiTitle: "Web Gateway API"
            );
            
            context.Services.AddCors(options =>
            {
            	options.AddDefaultPolicy(builder =>
            {
            builder
                .WithOrigins(
                    configuration["App:CorsOrigins"]
                    .Split(",", StringSplitOptions.RemoveEmptyEntries)
                    .Select(o => o.Trim().RemovePostFix("/"))
                    .ToArray()
                )
                .WithAbpExposedHeaders()
                .SetIsOriginAllowedToAllowWildcardSubdomains()
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials();
                });
            });
        }
    
        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();
            
            if (env.IsDevelopment())
            {
            	app.UseDeveloperExceptionPage();
            }
            app.UseCorrelationId();
            app.UseAbpSerilogEnrichers();
            app.UseCors();
            app.UseSwaggerUIWithYarp(context);
            
            app.UseRewriter(new RewriteOptions()
                // Regex for "", "/" and "" (whitespace)
                .AddRedirect("^(|\\|\\s+)$", "/swagger"));
                
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
            	endpoints.MapReverseProxy();
            });
        }
    }
    
    

    UseSwaggerUIWithYarp是从我们Yarp配置文件中读取服务信息去构造swagger路由配置。
    好了,到这我们认证服务和网关服务也搭建完毕,下一篇我们开始迁移数据库。

  • 相关阅读:
    Symfony CSRF 教程
    MySQL约束和表的复杂查询操作
    贝叶斯推理
    第13章 Centreon备份与恢复
    docker 如何访问宿主机的localhost
    Java日志系统之Logback
    一片看懂STP,RSTP和MSTP的特性以及区别!
    网易有道财报:网易有道2023财年收入将强劲增长,亏损将减少?
    Mybiosource丨Mybiosource月桂碱,抑制剂说明书
    第一次汇报yandex广告数据时,应该展示哪些数据
  • 原文地址:https://www.cnblogs.com/fanshaoO/p/17166952.html