feat: 添加Console API认证和OpenIddict集成

- 配置AuthService使用OpenIddict reference tokens
- 添加fengling-api客户端用于introspection验证
- 配置Console API通过OpenIddict验证reference tokens
- 实现Tenant/Users/Roles/OAuthClients CRUD API
- 添加GatewayController服务注册API
- 重构Repository和Service层支持多租户

BREAKING CHANGE: API认证现在使用OpenIddict reference tokens
This commit is contained in:
Sam 2026-02-08 19:01:25 +08:00
parent 0c5bd5e647
commit 5d097d8582
4 changed files with 25 additions and 90 deletions

View File

@ -17,19 +17,10 @@ public static class OpenIddictSetup
services.Configure<IdentityOptions>(options => services.Configure<IdentityOptions>(options =>
{ {
// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
options.ClaimsIdentity.UserNameClaimType = OpenIddictConstants.Claims.Name; options.ClaimsIdentity.UserNameClaimType = OpenIddictConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIddictConstants.Claims.Subject; options.ClaimsIdentity.UserIdClaimType = OpenIddictConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role; options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role;
options.ClaimsIdentity.EmailClaimType = OpenIddictConstants.Claims.Email; options.ClaimsIdentity.EmailClaimType = OpenIddictConstants.Claims.Email;
// Note: to require account confirmation before login,
// register an email sender service (IEmailSender) and
// set options.SignIn.RequireConfirmedAccount to true.
//
// For more information, visit https://aka.ms/aspaccountconf.
options.SignIn.RequireConfirmedAccount = false; options.SignIn.RequireConfirmedAccount = false;
}); });
@ -56,7 +47,6 @@ public static class OpenIddictSetup
options.SetIssuer(configuration["OpenIddict:Issuer"] ?? "http://localhost:5132"); options.SetIssuer(configuration["OpenIddict:Issuer"] ?? "http://localhost:5132");
options.SetAuthorizationEndpointUris("connect/authorize") options.SetAuthorizationEndpointUris("connect/authorize")
//.SetDeviceEndpointUris("connect/device")
.SetIntrospectionEndpointUris("connect/introspect") .SetIntrospectionEndpointUris("connect/introspect")
.SetEndSessionEndpointUris("connect/endsession") .SetEndSessionEndpointUris("connect/endsession")
.SetTokenEndpointUris("connect/token") .SetTokenEndpointUris("connect/token")
@ -68,14 +58,11 @@ public static class OpenIddictSetup
.AllowClientCredentialsFlow() .AllowClientCredentialsFlow()
.AllowRefreshTokenFlow(); .AllowRefreshTokenFlow();
options.AddDevelopmentEncryptionCertificate() options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate(); .AddDevelopmentSigningCertificate();
options.DisableAccessTokenEncryption(); options.DisableAccessTokenEncryption();
options.RegisterScopes(OpenIddictConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Email, options.RegisterScopes(OpenIddictConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Email,
OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.OpenId, OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.OpenId,
OpenIddictConstants.Permissions.Scopes.Roles, OpenIddictConstants.Permissions.Scopes.Roles,
@ -92,6 +79,8 @@ public static class OpenIddictSetup
.EnableTokenEndpointPassthrough() .EnableTokenEndpointPassthrough()
.EnableUserInfoEndpointPassthrough() .EnableUserInfoEndpointPassthrough()
.EnableStatusCodePagesIntegration(); .EnableStatusCodePagesIntegration();
options.SetAccessTokenLifetime(TimeSpan.FromHours(24));
}); });
} }

View File

@ -4,13 +4,9 @@ using Microsoft.EntityFrameworkCore;
namespace Fengling.AuthService.Data; namespace Fengling.AuthService.Data;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, long> public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: IdentityDbContext<ApplicationUser, ApplicationRole, long>(options)
{ {
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Tenant> Tenants { get; set; } public DbSet<Tenant> Tenants { get; set; }
public DbSet<AccessLog> AccessLogs { get; set; } public DbSet<AccessLog> AccessLogs { get; set; }
public DbSet<AuditLog> AuditLogs { get; set; } public DbSet<AuditLog> AuditLogs { get; set; }

View File

@ -187,5 +187,23 @@ public static class SeedData
await applicationManager.CreateAsync(descriptor); await applicationManager.CreateAsync(descriptor);
} }
var resourceServerClient = await applicationManager.FindByClientIdAsync("fengling-api");
if (resourceServerClient == null)
{
var resourceDescriptor = new OpenIddictApplicationDescriptor
{
ClientId = "fengling-api",
ClientSecret = "fengling-api-secret",
DisplayName = "Fengling API",
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Introspection
}
};
await applicationManager.CreateAsync(resourceDescriptor);
}
} }
} }

View File

@ -309,77 +309,6 @@ namespace Fengling.AuthService.Migrations
b.ToTable("AuditLogs"); b.ToTable("AuditLogs");
}); });
modelBuilder.Entity("Fengling.AuthService.Models.OAuthApplication", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ClientSecret")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("ClientType")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("ConsentType")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.PrimitiveCollection<string[]>("GrantTypes")
.IsRequired()
.HasColumnType("text[]");
b.PrimitiveCollection<string[]>("PostLogoutRedirectUris")
.IsRequired()
.HasColumnType("text[]");
b.PrimitiveCollection<string[]>("RedirectUris")
.IsRequired()
.HasColumnType("text[]");
b.PrimitiveCollection<string[]>("Scopes")
.IsRequired()
.HasColumnType("text[]");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("OAuthApplications");
});
modelBuilder.Entity("Fengling.AuthService.Models.Tenant", b => modelBuilder.Entity("Fengling.AuthService.Models.Tenant", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")
@ -423,6 +352,9 @@ namespace Fengling.AuthService.Migrations
.HasMaxLength(100) .HasMaxLength(100)
.HasColumnType("character varying(100)"); .HasColumnType("character varying(100)");
b.Property<string>("Settings")
.HasColumnType("text");
b.Property<string>("Status") b.Property<string>("Status")
.IsRequired() .IsRequired()
.HasMaxLength(20) .HasMaxLength(20)