当前位置: 首页 > wzjs >正文

动易政府网站源代码鱼头seo软件

动易政府网站源代码,鱼头seo软件,网站管理密码,手机网站自动跳转ABP vNext OpenIddict:多租户授权中心 🚀 📚 目录ABP vNext OpenIddict:多租户授权中心 🚀TL;DR 🎯1. 环境与依赖 🛠️2. 系统架构概览 🏗️3. 模块依赖配置 🔧4. Prog…

ABP vNext + OpenIddict:多租户授权中心 🚀


📚 目录

  • ABP vNext + OpenIddict:多租户授权中心 🚀
    • TL;DR 🎯
    • 1. 环境与依赖 🛠️
    • 2. 系统架构概览 🏗️
    • 3. 模块依赖配置 🔧
    • 4. Program.cs 配置 ⚙️
      • 中间件管道流程图 🛣️
    • 5. CustomOpenIddictDbContext 与多租户隔离 🌐
    • 6. Migration:添加 TenantId 列 🗄️
    • 7. 客户端与范围管理 📦
    • 8. 外部登录整合与用户映射 🔄
    • 9. 细粒度访问控制 🔒
    • 10. 后台 Token 清理服务 🧹
    • 11. 集成测试与 CI/CD 🔍
      • 11.1 集成测试示例
      • 11.2 Pipeline 示例
    • 12. ReadCertificateFromStore 示例 📜


TL;DR 🎯

  • 多租户隔离:EF Core “影子属性” + 全局查询过滤 + SaveChanges 钩子,保障所有 OpenIddict 实体按当前租户过滤
  • 🔑 OpenIddict 集成AbpOpenIddictModule 一行启用 Core/Server/Validation,并整合 ASP.NET Identity
  • 🔒 安全加固:Azure Key Vault 签名证书、严格 CORS/CSP/HSTS、生产环境证书示例
  • 🚀 高性能可复现:Redis 分布式缓存、刷新令牌滚动策略、后台 Token 清理 Hosted Service
  • 测试 & CI/CD:集成测试覆盖租户隔离与授权流程,GitHub Actions 自动化 Migrations → 测试 → 部署

1. 环境与依赖 🛠️

  • 目标框架:.NET 8.0 +

  • ABP 版本:8.x +

  • 核心 NuGet 包

    dotnet add package Volo.Abp.OpenIddict --version 8.*
    dotnet add package Volo.Abp.OpenIddict.EntityFrameworkCore --version 8.*
    dotnet add package Volo.Abp.TenantManagement.Domain --version 8.*
    dotnet add package Microsoft.AspNetCore.Authentication.Google
    dotnet add package Microsoft.Identity.Web
    dotnet add package StackExchange.Redis
    dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets
    
  • 数据库:EF Core + SQL Server / PostgreSQL

  • 缓存:Redis

  • 示例 ConnectionString

    "ConnectionStrings": {"Default": "Server=.;Database=AuthCenter;User Id=sa;Password=YourPwd;Max Pool Size=200;Command Timeout=60;"
    },
    "Redis": "localhost:6379"
    

2. 系统架构概览 🏗️

External
AuthCenter
JWT/OAuth2
OIDC
OIDC
Tenants
T1
T2
Google
AzureAD
OpenIddict Server
Host/Path/Header Resolver
受保护 API
Configuration Store
Cache
  • Tenant Resolver:Host/Path/Header 多策略解析,注入 TenantId
  • Configuration Store:Clients、Scopes 存于 EF Core 表,结合“影子属性”按租户过滤
  • Redis 缓存:授权配置缓存、验证密钥缓存、Token 缓存

3. 模块依赖配置 🔧

using Volo.Abp;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.OpenIddict;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.TenantManagement.Domain;namespace AuthCenter
{[DependsOn(typeof(AbpOpenIddictModule),typeof(AbpOpenIddictEntityFrameworkCoreModule),typeof(AbpTenantManagementDomainModule))]public class AuthCenterModule : AbpModule{public override void ConfigureServices(ServiceConfigurationContext context){// 配置全局 EF Core 提供者(SQL Server)Configure<AbpDbContextOptions>(opts =>{opts.UseSqlServer();});}}
}

4. Program.cs 配置 ⚙️

var builder = WebApplication.CreateBuilder(args);// 1. Key Vault 集成(可选)🔐
var vaultUrl = builder.Configuration["KeyVault:Url"];
if (!string.IsNullOrEmpty(vaultUrl))
{builder.Configuration.AddAzureKeyVault(new Uri(vaultUrl), new DefaultAzureCredential());
}// 2. CORS 策略 🛡️
builder.Services.AddCors(opts =>
{opts.AddPolicy("AllowTenantApps", p =>p.WithOrigins("https://*.yourtenantdomain.com").AllowAnyHeader().AllowAnyMethod().AllowCredentials());
});// 3. EF Core + OpenIddict 实体 注册 🗄️
var connString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddDbContext<CustomOpenIddictDbContext>(options =>
{options.UseSqlServer(connString,sql => sql.MigrationsAssembly(typeof(AuthCenterModule).Assembly.FullName));options.UseOpenIddict();  // 确保 OpenIddict 实体映射
});
builder.Services.AddAbpDbContext<CustomOpenIddictDbContext>(opts =>
{opts.AddDefaultRepositories<OpenIddictEntityFrameworkCoreApplication>();
});// 4. OpenIddict 服务注册 🔑
builder.Services.AddAbpOpenIddict().AddCore(options =>{options.UseEntityFrameworkCore().UseDbContext<CustomOpenIddictDbContext>();}).AddServer(options =>{// Endpointsoptions.SetAuthorizationEndpointUris("/connect/authorize").SetTokenEndpointUris("/connect/token").SetLogoutEndpointUris("/connect/logout");// Flowsoptions.AllowAuthorizationCodeFlow().AllowRefreshTokenFlow();// 刷新令牌:30 天 + 滚动刷新options.SetRefreshTokenLifetime(TimeSpan.FromDays(30));options.UseRollingRefreshTokens();// 开发/生产证书 🎫if (builder.Environment.IsDevelopment()){options.AddDevelopmentEncryptionCertificate().AddDevelopmentSigningCertificate();}else{var thumb = builder.Configuration["SigningCertificateThumbprint"];options.AddSigningCertificate(ReadCertificateFromStore(thumb));}// ASP.NET Core 集成options.UseAspNetCore().EnableAuthorizationEndpointPassthrough().EnableTokenEndpointPassthrough().EnableLogoutEndpointPassthrough();// TenantId 写入:Authorization + Token 🌐options.AddEventHandler<OpenIddictServerEvents.HandleAuthorizationRequestContext>(builder =>builder.UseInlineHandler(ctx =>{var db       = ctx.Transaction.GetDbContext<CustomOpenIddictDbContext>();var tenantId = ctx.HttpContext.GetMultiTenantContext().TenantId;db.Entry(ctx.Authorization!).Property("TenantId").CurrentValue = tenantId;return default;}));options.AddEventHandler<OpenIddictServerEvents.HandleTokenRequestContext>(builder =>builder.UseInlineHandler(ctx =>{var db       = ctx.Transaction.GetDbContext<CustomOpenIddictDbContext>();var tenantId = ctx.HttpContext.GetMultiTenantContext().TenantId;db.Entry(ctx.Token!).Property("TenantId").CurrentValue = tenantId;return default;}));}).AddValidation(options =>{options.UseLocalServer();options.UseAspNetCore();});// 5. ASP.NET Identity 集成 👤
builder.Services.AddAbpIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<CustomOpenIddictDbContext>();// 6. Redis 缓存 & 多租户解析 🔄
builder.Services.AddStackExchangeRedisCache(opts =>
{opts.Configuration = builder.Configuration["Redis"];opts.InstanceName  = "AuthCenter:";
});
builder.Services.AddAbpMultiTenancy(opts =>
{opts.Resolvers.Add<HostTenantResolveContributor>();opts.Resolvers.Add<PathTenantResolveContributor>();opts.Resolvers.Add<HeaderTenantResolveContributor>();
});// 7. Token Cleanup 后台服务 🧹
builder.Services.AddHostedService<TokenCleanupService>();// 8. 认证与授权 🔒
builder.Services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
builder.Services.AddAuthorization();// 9. Controllers 🚀
builder.Services.AddControllers();var app = builder.Build();

中间件管道流程图 🛣️

UseHttpsRedirection
UseMultiTenancy
UseRouting
UseCors(AllowTenantApps)
UseAuthentication
UseAuthorization
MapControllers
(OpenIddict endpoints)
app.UseHttpsRedirection();
app.UseMultiTenancy();
app.UseRouting();
app.UseCors("AllowTenantApps");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

5. CustomOpenIddictDbContext 与多租户隔离 🌐

using Microsoft.EntityFrameworkCore;
using OpenIddict.EntityFrameworkCore.Models;
using OpenIddict.EntityFrameworkCore;
using Volo.Abp.MultiTenancy;public class CustomOpenIddictDbContext :OpenIddictEntityFrameworkCoreDbContext<CustomOpenIddictDbContext,OpenIddictEntityFrameworkCoreApplication,OpenIddictEntityFrameworkCoreAuthorization,OpenIddictEntityFrameworkCoreScope,OpenIddictEntityFrameworkCoreToken>,IMultiTenant
{public Guid? TenantId { get; set; }public CustomOpenIddictDbContext(DbContextOptions<CustomOpenIddictDbContext> options): base(options) { }protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(builder);// 影子属性 + 全局过滤void Configure<TEntity>() where TEntity : class{builder.Entity<TEntity>().Property<Guid?>("TenantId").HasColumnType("uniqueidentifier");builder.Entity<TEntity>().HasQueryFilter(e => EF.Property<Guid?>(e, "TenantId") == TenantId);}Configure<OpenIddictEntityFrameworkCoreApplication>();Configure<OpenIddictEntityFrameworkCoreAuthorization>();Configure<OpenIddictEntityFrameworkCoreScope>();Configure<OpenIddictEntityFrameworkCoreToken>();}public override int SaveChanges(bool acceptAllChangesOnSuccess){SetTenantId();return base.SaveChanges(acceptAllChangesOnSuccess);}public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,CancellationToken cancellationToken = default){SetTenantId();return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);}private void SetTenantId(){foreach (var entry in ChangeTracker.Entries()){if (entry.State != EntityState.Added) continue;var type = entry.Entity.GetType();if (type == typeof(OpenIddictEntityFrameworkCoreApplication) ||type == typeof(OpenIddictEntityFrameworkCoreAuthorization) ||type == typeof(OpenIddictEntityFrameworkCoreScope) ||type == typeof(OpenIddictEntityFrameworkCoreToken)){entry.Property("TenantId").CurrentValue = TenantId;}}}
}

6. Migration:添加 TenantId 列 🗄️

public partial class AddTenantIdToOpenIddict : Migration
{protected override void Up(MigrationBuilder mb){mb.AddColumn<Guid>(name: "TenantId", table: "OpenIddictApplications", type: "uniqueidentifier", nullable: true);mb.AddColumn<Guid>(name: "TenantId", table: "OpenIddictAuthorizations", type: "uniqueidentifier", nullable: true);mb.AddColumn<Guid>(name: "TenantId", table: "OpenIddictScopes", type: "uniqueidentifier", nullable: true);mb.AddColumn<Guid>(name: "TenantId", table: "OpenIddictTokens", type: "uniqueidentifier", nullable: true);}protected override void Down(MigrationBuilder mb){mb.DropColumn("TenantId", "OpenIddictApplications");mb.DropColumn("TenantId", "OpenIddictAuthorizations");mb.DropColumn("TenantId", "OpenIddictScopes");mb.DropColumn("TenantId", "OpenIddictTokens");}
}

执行:

dotnet ef migrations add AddTenantId -c CustomOpenIddictDbContext -o Migrations/OpenIddictDb
dotnet ef database update --context CustomOpenIddictDbContext

7. 客户端与范围管理 📦

public async Task RegisterApplicationAsync(Guid tenantId, string clientUri)
{using var scope = _serviceProvider.CreateScope();var db      = scope.ServiceProvider.GetRequiredService<CustomOpenIddictDbContext>();db.TenantId = tenantId;var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictEntityFrameworkCoreApplication>>();var descriptor = new OpenIddictApplicationDescriptor{ClientId               = $"{tenantId}_web",DisplayName            = "Tenant Web App",RedirectUris           = { new Uri($"{clientUri}/signin-oidc") },PostLogoutRedirectUris = { new Uri($"{clientUri}/signout-callback-oidc") },Permissions ={OpenIddictConstants.Permissions.Endpoints.Authorization,OpenIddictConstants.Permissions.Endpoints.Token,OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,OpenIddictConstants.Permissions.GrantTypes.RefreshToken,OpenIddictConstants.Permissions.Scopes.Profile,OpenIddictConstants.Permissions.Scopes.Email,OpenIddictConstants.Permissions.Scopes.OfflineAccess}};await manager.CreateAsync(descriptor);// 写入 TenantIdvar entity = await manager.FindByClientIdAsync(descriptor.ClientId);db.Entry(entity!).Property<Guid?>("TenantId").CurrentValue = tenantId;await db.SaveChangesAsync();
}

8. 外部登录整合与用户映射 🔄

builder.Services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme).AddGoogle("Google", opts =>{opts.ClientId     = builder.Configuration["Authentication:Google:ClientId"];opts.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];opts.SignInScheme = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme;opts.Events.OnTicketReceived = ctx =>ctx.HttpContext.RequestServices.GetRequiredService<ExternalUserMapper>().MapAsync(ctx);}).AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));builder.Services.AddScoped<ExternalUserMapper>();public class ExternalUserMapper
{private readonly UserManager<ApplicationUser> _um;public ExternalUserMapper(UserManager<ApplicationUser> um) => _um = um;public async Task MapAsync(TicketReceivedContext ctx){var principal = ctx.Principal;var email     = principal.FindFirstValue(ClaimTypes.Email);var tenantId  = ctx.HttpContext.GetMultiTenantContext().TenantId;var user      = await _um.FindByEmailAsync(email)?? new ApplicationUser { TenantId = tenantId, UserName = email, Email = email };if (user.Id == default) await _um.CreateAsync(user);}
}
Client AppAuthCenterGoogle/AzureDatabase/connect/authorizeRedirect for loginCallback with ticketExternalUserMapper.MapAsyncPersist userReturn tokensClient AppAuthCenterGoogle/AzureDatabase

9. 细粒度访问控制 🔒

builder.Services.AddAuthorization(opts =>
{opts.AddPolicy("TenantAdmin", policy =>policy.RequireClaim("tenant_id").RequireRole("Admin"));
});// 在 AccessToken 生成前注入自定义 Claim
builder.Services.AddOpenIddict().AddServer(options =>{options.AddEventHandler<OpenIddictServerEvents.SerializeAccessTokenContext>(builder =>builder.UseInlineHandler(ctx =>{var db     = ctx.Transaction.GetDbContext<CustomOpenIddictDbContext>();var userId = ctx.Principal.GetClaim(OpenIddictConstants.Claims.Subject);var user   = db.Set<ApplicationUser>().Find(Guid.Parse(userId));ctx.Principal.SetClaim("roles", string.Join(",", user?.Roles ?? Array.Empty<string>()));return default;}));});

10. 后台 Token 清理服务 🧹

public class TokenCleanupService : IHostedService, IDisposable
{private readonly IServiceProvider _sp;private Timer? _timer;public TokenCleanupService(IServiceProvider sp) => _sp = sp;public Task StartAsync(CancellationToken _) {_timer = new Timer(Cleanup, null, TimeSpan.Zero, TimeSpan.FromHours(1));return Task.CompletedTask;}private async void Cleanup(object? _) {using var scope = _sp.CreateScope();var db = scope.ServiceProvider.GetRequiredService<CustomOpenIddictDbContext>();var expired = db.Set<OpenIddictEntityFrameworkCoreToken>().Where(t => t.Status != OpenIddictConstants.Statuses.Valid ||t.Revoked || t.CreationDate < DateTimeOffset.UtcNow.AddDays(-30));db.RemoveRange(expired);await db.SaveChangesAsync();}public Task StopAsync(CancellationToken _) {_timer?.Change(Timeout.Infinite, 0);return Task.CompletedTask;}public void Dispose() => _timer?.Dispose();
}

11. 集成测试与 CI/CD 🔍

11.1 集成测试示例

public class AuthCenterTests : IClassFixture<WebApplicationFactory<Program>>
{private readonly HttpClient _client;public AuthCenterTests(WebApplicationFactory<Program> f) => _client = f.CreateClient();[Fact]public async Task TenantA_CannotUse_TenantB_AuthorizationCode(){_client.DefaultRequestHeaders.Add("X-Tenant-ID","tenantA");var codeResp = await _client.PostAsync("/connect/authorize?client_id=tenantB_web&response_type=code&scope=openid", null);Assert.Equal(HttpStatusCode.BadRequest, codeResp.StatusCode);}
}

11.2 Pipeline 示例

name: CIon: [push,pull_request]jobs:build-and-test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3- name: Setup .NETuses: actions/setup-dotnet@v2with: dotnet-version: '8.0.x'- name: Restore & Buildrun: dotnet restore && dotnet build --no-restore- name: EF Migrationsrun: dotnet ef database update -c CustomOpenIddictDbContext- name: Validate Certificate/Licenserun: dotnet run --project src/AuthCenter -- --validate-license- name: Run Testsrun: dotnet test --no-build --verbosity normal

12. ReadCertificateFromStore 示例 📜

static X509Certificate2 ReadCertificateFromStore(string thumbprint)
{using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);store.Open(OpenFlags.ReadOnly);var certs = store.Certificates.Find(X509FindType.FindByThumbprint,thumbprint,validOnly: false);if (certs.Count == 0)throw new InvalidOperationException($"Certificate with thumbprint {thumbprint} not found");return certs[0];
}

http://www.dtcms.com/wzjs/145515.html

相关文章:

  • 三五互联网站建设垃圾网络营销策略案例
  • 品牌设计公司是做什么的seo排名培训
  • 网站制作软件手机it培训机构培训费用
  • 做商品网站的教学视频seo实战指导
  • 福州企业做网站百度老年搜索
  • 孝感市建设局网站seo网站优化详解
  • 政府门户网站建设论文网络营销推广计划
  • 网络代理修复怎么解决杭州seo优化
  • 功能网站首页模板站长工具友链检测
  • 茶叶网站建设模板百度竞价开户3000
  • 广东手机网站建设价格阿里巴巴logo
  • 张家港市规划建设网站做微商如何引流推广怎么找客源
  • 网站两个域名网络推广营销方式
  • 绿色大气网站百度网站排名搜行者seo
  • 专业做破碎机的网站怎么引流推广自己的产品
  • 郑州市建设局网站指数计算器
  • 免费的ppt下载网站网站搜索优化找哪家
  • 提供模板网站制作多少钱免费发布友链
  • 触屏手机网站seo文章代写平台
  • ppt模板哪里可以免费下载厦门seo屈兴东
  • 宝安中心做网站2023年4 5月份疫情结束吗
  • 本溪做网站 淘宝店站长工具端口扫描
  • 网站弹窗代码网站seo快速排名优化
  • 付费阅读网站建设百度入驻商家
  • 济南网站建设联系小七seo搜索优化服务
  • 浙江金华网站建设河北seo技术
  • iis提示网站建设中上海疫情最新情况
  • 福州网站建设推广semir是什么品牌
  • 有什么做动画的网站上海seo优化公司kinglink
  • 武汉网站公司沧州网站推广优化