Admin.NET开源版微服务改造记录
将Admin.NET.Core项目拆分成两个项目:Admin.NET.Common,Admin.NET.Core
Admin.NET.Common放基础工具类
Admin.NET.Core放框架核心类库
AspireApp.AppHost中的AppHost.cs配置:
- using Aspire.Hosting;
- using Aspire.Hosting.Dapr;
- using AspireApp.AppHost;
- var builder = DistributedApplication.CreateBuilder(args);
- var postgresQL = builder.AddPostgres("postgresQL")
- .WithImage("ankane/pgvector")
- .WithImageTag("latest")
- .WithLifetime(ContainerLifetime.Persistent)
- .WithHealthCheck()
- .WithPgWeb();
- var postgres = postgresQL.AddDatabase("postgres");
- var postgres2 = postgresQL.AddDatabase("postgres2");
- //var redis = builder.AddRedis("redis").WithLifetime(ContainerLifetime.Persistent)
- // .WithHealthCheck()
- // .WithRedisCommander();
- // 使用 RabbitMQ 作为 Pub/Sub 组件
- //var rabbitmq = builder.AddRabbitMQ("rabbitmq")
- // .WithLifetime(ContainerLifetime.Persistent)
- // .WithHealthCheck()
- // .WithManagementPlugin();
- // 1. 定义共享目录的绝对路径(建议指向 admin-net-core 的实际目录或独立的 shared 目录)
- var uploadPath = Path.GetFullPath("../Admin.NET/Admin.NET.Core/wwwroot/upload");
- // 确保目录存在
- if (!Directory.Exists(uploadPath))
- {
- Directory.CreateDirectory(uploadPath);
- }
- // Admin.NET.Core 使用 Furion 的 Knife4j UI,不使用 Aspire 的 Swagger UI
- var core = builder.AddProject<Projects.Admin_NET_Core>("admin-net-core")
- .WithReference(postgres)
- .WaitFor(postgres)
- .WithSwaggerUI();
- var baseApi = builder.AddProject<Projects.Base>("base")
- .WithReference(postgres2)
- .WithSwaggerUI()
- .WithReference(core)
- .WaitFor(postgres2)
- .WaitFor(core);
- var yarp = builder.AddProject<Projects.AspireApp_Yarp>("aspireapp-yarp")
- .WithEndpoint( port: 5008, scheme: "http", name: "yarp-http") // 指定唯一名称
- .WithEndpoint( port: 7008, scheme: "https", name: "yarp-https") // 指定唯一名称
- .WithReference(core)
- .WithReference(baseApi)
- .WaitFor(core)
- .WaitFor(baseApi);
- //var frontend = builder.AddNodeApp(
- // "frontend",
- // scriptPath: "scripts/run-pnpm.cjs",
- // workingDirectory: "../Web",
- // args: new[] { "dev" }
- // )
- // .WithReference(yarp)
- // .WaitFor(yarp);
- builder.Build().Run();
复制代码 AIP网关:AspireApp.Yarp
Program.cs- var builder = WebApplication.CreateBuilder(args);
- builder.AddServiceDefaults();
- builder.Services.AddReverseProxy()
- .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
- .AddServiceDiscoveryDestinationResolver();
- var app = builder.Build();
- app.MapDefaultEndpoints();
- app.MapReverseProxy();
- app.MapGet("/", () => "Hello World!");
- app.Run();
复制代码 appsettings.json- {
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- },
- "AllowedHosts": "*",
- "ReverseProxy": {
- "Routes": {
- "core": {
- "ClusterId": "core",
- "Match": {
- "Path": "/core/{**remainder}"
- },
- "Transforms": [
- { "PathRemovePrefix": "/core" },
- { "PathPrefix": "/" },
- { "RequestHeaderOriginalHost": "true" }
- ]
- },
- "base": {
- "ClusterId": "base",
- "Match": {
- "Path": "/base/{**remainder}"
- },
- "Transforms": [
- { "PathRemovePrefix": "/base" },
- { "PathPrefix": "/" },
- { "RequestHeaderOriginalHost": "true" }
- ]
- }
- },
- "Clusters": {
- "core": {
- "Destinations": {
- "base_destination": {
- "Address": "http+https://admin-net-core"
- }
- }
- },
- "base": {
- "Destinations": {
- "base_destination": {
- "Address": "http+https://base"
- }
- }
- }
- }
- }
- }
复制代码 业务项目(base项目)调用核心项目(core项目)的方法
我们使用Refit来服务调用
在base项目中的Startup.cs中- // 注册 JwtTokenHandler - 用于自动添加 JWT Token 到 Refit 请求
- services.AddTransient<JwtTokenHandler>();
-
- // 配置 Refit 客户端并添加 JWT Token 处理器
- //系统配置
- services.AddRefitClient<IConfigService>(refitSettings)
- .ConfigureHttpClient(c => c.BaseAddress = new("https+http://admin-net-core"))
- .AddHttpMessageHandler<JwtTokenHandler>();
- //系统菜单
- services.AddRefitClient<IMenuService>(refitSettings)
- .ConfigureHttpClient(c => c.BaseAddress = new("https+http://admin-net-core"))
- .AddHttpMessageHandler<JwtTokenHandler>();
- //组织机构
- services.AddRefitClient<IOrgService>(refitSettings)
- .ConfigureHttpClient(c => c.BaseAddress = new("https+http://admin-net-core"))
- .AddHttpMessageHandler<JwtTokenHandler>();
- //文件
- services.AddRefitClient<IFileService>(refitSettings)
- .ConfigureHttpClient(c => c.BaseAddress = new("https+http://admin-net-core"))
- .AddHttpMessageHandler<JwtTokenHandler>();
- services.AddRefitClient<ITest>(refitSettings)
- .ConfigureHttpClient(c => c.BaseAddress = new("https+http://apiservice"))
- .AddHttpMessageHandler<JwtTokenHandler>();
复制代码 其中最重要的权限接口调用- using Refit;
- using GetAttribute = Refit.GetAttribute;
- namespace Base.Rest;
- public interface IMenuService
- {
- [Get("/api/sysMenu/getOwnBtnPermList")]
- Task<List<string>> GetOwnBtnPermList();
- [Get("/api/sysMenu/getAllBtnPermList")]
- Task<List<string>> GetAllBtnPermList();
- }
复制代码 修改base项目的权限验证方法JwtHandler.cs
IConfigService IMenuService 就是我们定义的Refit接口,这样,每次权限验证就会调用core项目的API接口- using Admin.NET.Core;
- using Admin.NET.Core.Service;
- using Base.Rest;
- using Furion;
- using Furion.Authorization;
- using Furion.DataEncryption;
- using Microsoft.AspNetCore.Authorization;
- using Microsoft.AspNetCore.Http;
- using System;
- using System.Threading.Tasks;
- namespace Admin.NET.Application;
- public class JwtHandler : AppAuthorizeHandler
- {
- private readonly SysCacheService _sysCacheService = App.GetRequiredService<SysCacheService>();
- private readonly IConfigService _sysConfigService = App.GetRequiredService<IConfigService>();
- private static readonly IMenuService SysMenuService = App.GetRequiredService<IMenuService>();
- /// <summary>
- /// 自动刷新Token
- /// </summary>
- /// <param name="context"></param>
- /// <param name="httpContext"></param>
- /// <returns></returns>
- public override async Task HandleAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
- {
- // 若当前账号存在黑名单中则授权失败
- if (_sysCacheService.ExistKey($"{CacheConst.KeyBlacklist}{context.User.FindFirst(ClaimConst.UserId)?.Value}"))
- {
- context.Fail();
- context.GetCurrentHttpContext().SignoutToSwagger();
- return;
- }
- var tokenExpire = await _sysConfigService.GetTokenExpire();
- var refreshTokenExpire = await _sysConfigService.GetRefreshTokenExpire();
- if (JWTEncryption.AutoRefreshToken(context, context.GetCurrentHttpContext(), tokenExpire.Result, refreshTokenExpire.Result))
- {
- await AuthorizeHandleAsync(context);
- }
- else
- {
- context.Fail(); // 授权失败
- var currentHttpContext = context.GetCurrentHttpContext();
- if (currentHttpContext == null) return;
- // 跳过由于 SignatureAuthentication 引发的失败
- if (currentHttpContext.Items.ContainsKey(SignatureAuthenticationDefaults.AuthenticateFailMsgKey)) return;
- currentHttpContext.SignoutToSwagger();
- }
- }
- public override async Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
- {
- // 已自动验证 Jwt Token 有效性
- return await CheckAuthorizeAsync(httpContext);
- }
- /// <summary>
- /// 权限校验核心逻辑
- /// </summary>
- /// <param name="httpContext"></param>
- /// <returns></returns>
- private static async Task<bool> CheckAuthorizeAsync(DefaultHttpContext httpContext)
- {
- // 登录模式判断PC、APP
- if (App.User.FindFirst(ClaimConst.LoginMode)?.Value == ((int)LoginModeEnum.APP).ToString())
- return true;
- // 排除超管
- if (App.User.FindFirst(ClaimConst.AccountType)?.Value == ((int)AccountTypeEnum.SuperAdmin).ToString())
- return true;
- // 路由名称
- var routeName = httpContext.Request.Path.StartsWithSegments("/api")
- ? httpContext.Request.Path.Value![5..].Replace("/", ":")
- : httpContext.Request.Path.Value![1..].Replace("/", ":");
- // 获取用户拥有按钮权限集合
- var ownBtnPermList = await SysMenuService.GetOwnBtnPermList();
- if (ownBtnPermList.Exists(u => routeName.Equals(u, StringComparison.CurrentCultureIgnoreCase)))
- return true;
- // 获取系统所有按钮权限集合
- var allBtnPermList = await SysMenuService.GetAllBtnPermList();
- return allBtnPermList.TrueForAll(u => !routeName.Equals(u, StringComparison.CurrentCultureIgnoreCase));
- }
- }
复制代码 项目地址
AspireApp: Admin.NET微服务版
Admin.NET:
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |