From e51ea08c8f64ca1df1fdbcf18259604ce31f61df Mon Sep 17 00:00:00 2001 From: Sam <315859133@qq.com> Date: Mon, 2 Feb 2026 01:04:51 +0800 Subject: [PATCH] chore: upgrade all projects to .NET 10.0 and latest packages --- docs/plans/fengling-console-plan.md | 157 +++++++++++++++ docs/task-01-create-project-structure.md | 12 +- docs/task-05-create-openiddict-endpoints.md | 186 ++++++++++++++++++ .../Fengling.AuthService.csproj | 10 +- src/YarpGateway/YarpGateway.csproj | 12 +- .../Debug/net10.0/YarpGateway.AssemblyInfo.cs | 2 +- .../YarpGateway.AssemblyInfoInputs.cache | 2 +- .../Debug/net10.0/YarpGateway.assets.cache | Bin 46102 -> 46102 bytes 8 files changed, 362 insertions(+), 19 deletions(-) create mode 100644 docs/plans/fengling-console-plan.md create mode 100644 docs/task-05-create-openiddict-endpoints.md diff --git a/docs/plans/fengling-console-plan.md b/docs/plans/fengling-console-plan.md new file mode 100644 index 0000000..b9a0d17 --- /dev/null +++ b/docs/plans/fengling-console-plan.md @@ -0,0 +1,157 @@ +# Fengling.Console 运管中心规划 + +## 项目概述 + +**项目名称**: Fengling.Console +**中文名称**: 风铃运管中心 +**定位**: 统一运维管理平台 + +## 架构整合 + +### 1. 后端整合 +- **原项目**: `src/YarpGateway/` (YARP Gateway) +- **整合到**: `src/Fengling.Console/` (新建) +- **新增功能**: OAuth Client管理 + +### 2. 前端整合 +- **原项目**: `src/YarpGateway.Admin/` (Vue 3 + Vite) +- **整合到**: `src/Fengling.Console.Web/` (新建) +- **功能扩展**: + - 网关路由管理 + - 租户管理 + - OAuth Client管理 + - 用户管理(通过AuthService API) + +## 功能模块 + +### 模块1: 网关管理 +- 租户列表 +- 租户路由配置 +- 集群实例管理 +- 全局路由配置 +- 负载均衡策略 + +### 模块2: OAuth Client管理 +- Client ID/Secret管理 +- 重定向URI配置 +- 授权类型配置 +- Scope配置 +- Client状态管理 + +### 模块3: 用户管理 +- 用户列表 +- 用户角色分配 +- 租户分配 +- 用户状态管理 + +## 认证集成 + +### Fengling.Console作为OAuth Client +需要在AuthService初始化时注册: + +```csharp +// Client配置 +ClientId: "fengling-console" +ClientSecret: "console-secret-xxx" +RedirectUris: ["http://console.fengling.local/auth/callback"] +PostLogoutRedirectUris: ["http://console.fengling.local/"] +Scopes: ["api", "offline_access"] +GrantTypes: ["authorization_code", "refresh_token"] +``` + +## 目录结构 + +``` +src/ +├── Fengling.AuthService/ # 认证服务(已完成部分) +├── Fengling.Console/ # 运管中心后端(新建) +│ ├── Controllers/ +│ │ ├── Gateway/ # 网关管理 +│ │ ├── OAuth/ # OAuth Client管理 +│ │ └── Users/ # 用户管理代理 +│ ├── Services/ +│ └── Data/ +├── Fengling.Console.Web/ # 运管中心前端(新建) +│ ├── src/ +│ │ ├── views/ +│ │ │ ├── gateway/ # 网关管理 +│ │ │ ├── oauth/ # OAuth管理 +│ │ │ └── users/ # 用户管理 +│ │ └── components/ +│ └── YarpGateway.Admin/ # 原有代码迁移 +└── YarpGateway/ # 网关服务(保留) +``` + +## 技术栈 + +### 后端 (Fengling.Console) +- .NET 10.0 +- ASP.NET Core Web API +- EF Core 10.0 +- PostgreSQL +- OpenTelemetry + +### 前端 (Fengling.Console.Web) +- Vue 3 + TypeScript +- Vite +- Element Plus +- Pinia +- Vue Router + +## 实施计划 + +### Phase 1: 认证服务扩展 +1. [ ] Task 5-9: 完成AuthService基础功能 +2. [ ] Task 10: 添加OAuth Client模型和管理 +3. [ ] Task 11: 预注册Fengling.Console作为Client + +### Phase 2: 运管中心后端 +1. [ ] Task 12: 创建Fengling.Console项目 +2. [ ] Task 13: 迁移网关管理功能 +3. [ ] Task 14: 添加OAuth Client管理API +4. [ ] Task 15: 添加用户管理代理API + +### Phase 3: 运管中心前端 +1. [ ] Task 16: 创建Fengling.Console.Web项目 +2. [ ] Task 17: 迁移YarpGateway.Admin代码 +3. [ ] Task 18: 添加OAuth管理界面 +4. [ ] Task 19: 集成OAuth认证登录 + +## 依赖关系 + +``` +Fengling.Console.Web + ↓ (OAuth 2.0) +Fengling.Console + ↓ (数据库) +PostgreSQL + ↓ (路由配置) +YarpGateway +``` + +## 数据库设计 + +### OAuth Applications表 +```sql +CREATE TABLE oauth_applications ( + id BIGSERIAL PRIMARY KEY, + client_id VARCHAR(100) UNIQUE NOT NULL, + client_secret VARCHAR(200), + display_name VARCHAR(100), + redirect_uris TEXT[], + post_logout_redirect_uris TEXT[], + scopes TEXT[], + grant_types TEXT[], + client_type VARCHAR(20), + consent_type VARCHAR(20), + status VARCHAR(20), + created_at TIMESTAMP DEFAULT NOW() +); +``` + +## 注意事项 + +1. **向后兼容**: YarpGateway继续独立运行 +2. **渐进式迁移**: 先完成Console后端,再迁移前端 +3. **统一认证**: 所有管理界面通过AuthService OAuth登录 +4. **权限控制**: Console内部API也需要认证 diff --git a/docs/task-01-create-project-structure.md b/docs/task-01-create-project-structure.md index e3f93a8..9af0893 100644 --- a/docs/task-01-create-project-structure.md +++ b/docs/task-01-create-project-structure.md @@ -24,7 +24,7 @@ Edit: `src/Fengling.AuthService/Fengling.AuthService.csproj` ```xml - net9.0 + net10.0 enable enable @@ -32,8 +32,8 @@ Edit: `src/Fengling.AuthService/Fengling.AuthService.csproj` - - + + @@ -80,14 +80,14 @@ This is the first task of implementing the Fengling Authentication Service. We'r **Architecture**: ASP.NET Core Web API with OpenIddict for OAuth2/OIDC, PostgreSQL for user data. -**Tech Stack**: .NET 9.0, OpenIddict 7.2.0, EF Core 9.0, PostgreSQL, Serilog 9.0.0, OpenTelemetry 1.11.0. +**Tech Stack**: .NET 10.0, OpenIddict 7.2.0, EF Core 10.0, PostgreSQL, Serilog 9.0.0, OpenTelemetry 1.11.0. **Working Directory**: `/Users/movingsam/Fengling.Refactory.Buiding/src` ## Verification - [ ] Project file created successfully -- [ ] Target framework set to net9.0 +- [ ] Target framework set to net10.0 - [ ] All NuGet packages added (latest versions) - [ ] appsettings.json configured with connection string and OpenIddict settings - [ ] Project builds successfully @@ -97,4 +97,4 @@ This is the first task of implementing the Fengling Authentication Service. We'r - OpenIddict packages updated to 7.2.0 (latest) - All dependencies updated to latest versions -- Used .NET 9.0 as target framework +- Used .NET 10.0 as target framework diff --git a/docs/task-05-create-openiddict-endpoints.md b/docs/task-05-create-openiddict-endpoints.md new file mode 100644 index 0000000..cf713f7 --- /dev/null +++ b/docs/task-05-create-openiddict-endpoints.md @@ -0,0 +1,186 @@ +# Task 5: Create OpenIddict Endpoints + +## Task Description + +**Files:** +- Create: `src/Fengling.AuthService/Controllers/AuthorizationController.cs` + +## Implementation Steps + +### Step 1: Create authorization endpoints + +Create: `src/Fengling.AuthService/Controllers/AuthorizationController.cs` + +```csharp +using Fengling.AuthService.Models; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; +using static OpenIddict.Abstractions.OpenIddictConstants; + +namespace Fengling.AuthService.Controllers; + +public class AuthorizationController : Controller +{ + private readonly IOpenIddictApplicationManager _applicationManager; + private readonly IOpenIddictAuthorizationManager _authorizationManager; + private readonly IOpenIddictScopeManager _scopeManager; + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public AuthorizationController( + IOpenIddictApplicationManager applicationManager, + IOpenIddictAuthorizationManager authorizationManager, + IOpenIddictScopeManager scopeManager, + SignInManager signInManager, + UserManager userManager, + ILogger logger) + { + _applicationManager = applicationManager; + _authorizationManager = authorizationManager; + _scopeManager = scopeManager; + _signInManager = signInManager; + _userManager = userManager; + _logger = logger; + } + + [HttpPost("~/connect/token")] + [Produces("application/json")] + public async Task Exchange() + { + var request = HttpContext.GetOpenIddictServerRequest() ?? + throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); + + if (request.IsPasswordGrantType()) + { + var user = await _userManager.FindByNameAsync(request.Username); + if (user == null || user.IsDeleted) + { + return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "用户不存在" + })); + } + + var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, false); + if (!result.Succeeded) + { + return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "用户名或密码错误" + })); + } + + var principal = await _signInManager.CreateUserPrincipalAsync(user); + + var claims = new List + { + new(Claims.Subject, user.Id.ToString()), + new(Claims.Name, user.UserName ?? string.Empty), + new(Claims.Email, user.Email ?? string.Empty), + new("tenant_id", user.TenantId.ToString()) + }; + + var roles = await _userManager.GetRolesAsync(user); + foreach (var role in roles) + { + claims.Add(new Claim(Claims.Role, role)); + } + + principal.SetScopes(request.GetScopes()); + principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); + + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.UnsupportedGrantType, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "不支持的授权类型" + })); + } + + [HttpGet("~/connect/authorize")] + [HttpPost("~/connect/authorize")] + [IgnoreAntiforgeryToken] + public async Task Authorize() + { + var request = HttpContext.GetOpenIddictServerRequest() ?? + throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); + + var result = await HttpContext.AuthenticateAsync(); + if (result == null || !result.Succeeded) + { + return Challenge( + new AuthenticationProperties + { + RedirectUri = Request.Path + Request.QueryString + }, + OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + return Ok(new { message = "Authorization endpoint" }); + } + + [HttpPost("~/connect/logout")] + [ValidateAntiForgeryToken] + public async Task Logout() + { + await HttpContext.SignOutAsync(); + return SignOut(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } +} +``` + +### Step 2: Run to verify + +Run: +```bash +dotnet run +``` + +Test with curl: +```bash +curl -X POST http://localhost:5000/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=password" \ + -d "username=admin" \ + -d "password=Admin@123" \ + -d "scope=api offline_access" +``` +Expected: 400 error (user doesn't exist yet, but endpoint is working) + +### Step 3: Commit + +```bash +git add src/Fengling.AuthService/Controllers/AuthorizationController.cs +git commit -m "feat(auth): add OpenIddict authorization endpoints" +``` + +## Context + +This task creates OpenIddict OAuth2/OIDC endpoints including token exchange, authorize, and logout. The token endpoint supports password flow for direct authentication. + +**Tech Stack**: OpenIddict 7.2.0, ASP.NET Core + +## Verification + +- [ ] AuthorizationController created +- [ ] Token endpoint supports password flow +- [ ] Tenant ID added to token claims +- [ ] Build succeeds +- [ ] Committed to git + +## Notes + +- Token endpoint returns JWT with tenant_id claim +- Logout endpoint clears session diff --git a/src/Fengling.AuthService/Fengling.AuthService.csproj b/src/Fengling.AuthService/Fengling.AuthService.csproj index cf37dd5..6354e63 100644 --- a/src/Fengling.AuthService/Fengling.AuthService.csproj +++ b/src/Fengling.AuthService/Fengling.AuthService.csproj @@ -1,21 +1,21 @@ - net9.0 + net10.0 enable enable - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/src/YarpGateway/YarpGateway.csproj b/src/YarpGateway/YarpGateway.csproj index a498881..0f895bd 100644 --- a/src/YarpGateway/YarpGateway.csproj +++ b/src/YarpGateway/YarpGateway.csproj @@ -7,16 +7,16 @@ - - - runtime; build; native; contentfiles; analyzers; buildtransitive + + + runtime; build; native; contentfiles, analyzers; buildtransitive all - - + + - + diff --git a/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfo.cs b/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfo.cs index f5756e1..37145d6 100644 --- a/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfo.cs +++ b/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("YarpGateway")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d52b7c9a46ded51a443127feb64c42410418f05e")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] [assembly: System.Reflection.AssemblyProductAttribute("YarpGateway")] [assembly: System.Reflection.AssemblyTitleAttribute("YarpGateway")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfoInputs.cache b/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfoInputs.cache index 1758a04..86f7a85 100644 --- a/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfoInputs.cache +++ b/src/YarpGateway/obj/Debug/net10.0/YarpGateway.AssemblyInfoInputs.cache @@ -1 +1 @@ -1eeba3ffadd654a1cb0f5a3d5c67778e6f01b8ed8162ae7982a98e3e3c44e84f +8ec52e08f20064638c4db3c23d5adde50628e34c7dcaf8a439956f9b42f760de diff --git a/src/YarpGateway/obj/Debug/net10.0/YarpGateway.assets.cache b/src/YarpGateway/obj/Debug/net10.0/YarpGateway.assets.cache index 5bcfc754e352c135471e5754fd6ede0daa7a7466..df316d31ec56aacab3de8aadd471c292cf511392 100644 GIT binary patch delta 64 zcmV-G0Kfm1=mM7L0xnQXM?nk#007Zq2Pt~5x>s((N=Bw>^gSP;EEf=zCRIOGabGIv W2x@tM00093lRsxXk^wuhrE#<7uNJ)k delta 64 zcmV-G0Kfm1=mM7L0xnQXM?nk#006r