115 lines
4.8 KiB
C#
115 lines
4.8 KiB
C#
using System;
|
||
using Microsoft.AspNetCore.Identity;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||
using TakeoutSaaS.Domain.Identity.Entities;
|
||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||
using TakeoutSaaS.Infrastructure.Common.Extensions;
|
||
using TakeoutSaaS.Infrastructure.Common.Options;
|
||
using TakeoutSaaS.Infrastructure.Identity.Options;
|
||
using TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||
using TakeoutSaaS.Infrastructure.Identity.Services;
|
||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||
using DomainIdentityUser = TakeoutSaaS.Domain.Identity.Entities.IdentityUser;
|
||
|
||
namespace TakeoutSaaS.Infrastructure.Identity.Extensions;
|
||
|
||
/// <summary>
|
||
/// 身份认证基础设施注入。
|
||
/// </summary>
|
||
public static class ServiceCollectionExtensions
|
||
{
|
||
/// <summary>
|
||
/// 注册身份认证基础设施(数据库、Redis、JWT、限流等)。
|
||
/// </summary>
|
||
/// <param name="services">服务集合。</param>
|
||
/// <param name="configuration">配置源。</param>
|
||
/// <param name="enableMiniFeatures">是否启用小程序相关依赖(如微信登录)。</param>
|
||
/// <param name="enableAdminSeed">是否启用后台账号初始化。</param>
|
||
/// <returns>服务集合。</returns>
|
||
/// <exception cref="InvalidOperationException">配置缺失时抛出。</exception>
|
||
public static IServiceCollection AddIdentityInfrastructure(
|
||
this IServiceCollection services,
|
||
IConfiguration configuration,
|
||
bool enableMiniFeatures = false,
|
||
bool enableAdminSeed = false)
|
||
{
|
||
services.AddDatabaseInfrastructure(configuration);
|
||
services.AddPostgresDbContext<IdentityDbContext>(DatabaseConstants.IdentityDataSource);
|
||
|
||
var redisConnection = configuration.GetConnectionString("Redis");
|
||
if (string.IsNullOrWhiteSpace(redisConnection))
|
||
{
|
||
throw new InvalidOperationException("缺少 Redis 连接字符串配置。");
|
||
}
|
||
|
||
services.AddStackExchangeRedisCache(options =>
|
||
{
|
||
options.Configuration = redisConnection;
|
||
});
|
||
|
||
services.AddScoped<IIdentityUserRepository, EfIdentityUserRepository>();
|
||
services.AddScoped<IMiniUserRepository, EfMiniUserRepository>();
|
||
services.AddScoped<IRoleRepository, EfRoleRepository>();
|
||
services.AddScoped<IPermissionRepository, EfPermissionRepository>();
|
||
services.AddScoped<IUserRoleRepository, EfUserRoleRepository>();
|
||
services.AddScoped<IRolePermissionRepository, EfRolePermissionRepository>();
|
||
services.AddScoped<IRoleTemplateRepository, EfRoleTemplateRepository>();
|
||
services.AddScoped<IJwtTokenService, JwtTokenService>();
|
||
services.AddScoped<IRefreshTokenStore, RedisRefreshTokenStore>();
|
||
services.AddScoped<ILoginRateLimiter, RedisLoginRateLimiter>();
|
||
services.AddScoped<IPasswordHasher<DomainIdentityUser>, PasswordHasher<DomainIdentityUser>>();
|
||
|
||
services.AddOptions<JwtOptions>()
|
||
.Bind(configuration.GetSection("Identity:Jwt"))
|
||
.ValidateDataAnnotations()
|
||
.ValidateOnStart();
|
||
|
||
services.AddOptions<LoginRateLimitOptions>()
|
||
.Bind(configuration.GetSection("Identity:LoginRateLimit"))
|
||
.ValidateDataAnnotations()
|
||
.ValidateOnStart();
|
||
|
||
services.AddOptions<RefreshTokenStoreOptions>()
|
||
.Bind(configuration.GetSection("Identity:RefreshTokenStore"));
|
||
|
||
if (enableMiniFeatures)
|
||
{
|
||
services.AddOptions<WeChatMiniOptions>()
|
||
.Bind(configuration.GetSection("Identity:WeChatMini"))
|
||
.ValidateDataAnnotations()
|
||
.ValidateOnStart();
|
||
|
||
services.AddHttpClient<IWeChatAuthService, WeChatAuthService>(client =>
|
||
{
|
||
client.BaseAddress = new Uri("https://api.weixin.qq.com/");
|
||
client.Timeout = TimeSpan.FromSeconds(10);
|
||
});
|
||
}
|
||
|
||
if (enableAdminSeed)
|
||
{
|
||
services.AddOptions<AdminSeedOptions>()
|
||
.Bind(configuration.GetSection("Identity:AdminSeed"))
|
||
.ValidateDataAnnotations()
|
||
.ValidateOnStart();
|
||
|
||
services.AddHostedService<IdentityDataSeeder>();
|
||
}
|
||
|
||
return services;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 确保数据库连接已配置(Database 节或 ConnectionStrings)。
|
||
/// </summary>
|
||
/// <param name="configuration">配置源。</param>
|
||
/// <param name="dataSourceName">数据源名称。</param>
|
||
/// <exception cref="InvalidOperationException">未配置时抛出。</exception>
|
||
private static void EnsureDatabaseConnectionConfigured(IConfiguration configuration, string dataSourceName)
|
||
{
|
||
// 保留兼容接口,当前逻辑在 DatabaseConnectionFactory 中兜底并记录日志。
|
||
}
|
||
}
|