【IdentityServer4】数据持久化

前文中,我们所有的IdentityServer4配置都是在代码中写死的,在实际的生产环境中肯定不能这么处理。可以使用Entity Framework Core持久化配置和存储操作数据。
源码:https://gitee.com/core-demo/identity-server4

安装Nuget包:

IDS持久化:

Install-Package IdentityServer4.EntityFramework -Version 3.1.4

使用sqlserver存储数据:

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 6.0.4
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.4

Identity相关包,实现用户的存储和服务:

Install-Package IdentityServer4.AspNetIdentity -Version 3.1.4
Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore -Version 6.0.4

Context

IdentityServer4.EntityFramework

IdentityServer4.EntityFramework使用以下 DbContext 实现所需的存储和服务:

  • ConfigurationDbContext
    用于配置数据,如clientsresourcesscopes
  • PersistedGrantDbContext
    用于临时操作数据,如授权代码和刷新令牌

Identity

创建一个继承自IdentityDbContext<IdentityUser>的上下文,实现用户的存储和服务

修改代码

在前面基础上修改,删除Config中写死的配置

用户数据相关(Identity)

1、AddIdentity()添加Identity相关服务,并配置密码规则

//Identity
builder.Services.AddDbContext<AspNetAccountDbContext>(options =>
{
    options.UseSqlServer(connectionString);
});
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<AspNetAccountDbContext>()
    .AddDefaultTokenProviders();
builder.Services.Configure<IdentityOptions>(options =>
{
    // 密码复杂度配置
    options.Password.RequireDigit = false;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;
    options.Password.RequireLowercase = false;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = false;
});

2、添加Context、User

public class AspNetAccountDbContext : IdentityDbContext<ApplicationUser>
{
    public AspNetAccountDbContext(DbContextOptions<AspNetAccountDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}
public class ApplicationUser : IdentityUser
{ }

3、添加配置

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=IDC;User ID=sa;Password=123456;"
  }
}

4、数据库迁移

add-migration InitialAspNetAccountDbMigration -c AspNetAccountDbContext -o Data/Migrations/Application/AspNetAccountDb

配置数据、操作数据相关

1、注册服务

var migrationsAssembly = typeof(Config).Assembly.GetName().Name;
builder.Services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddConfigurationStore(options =>
{
    options.ConfigureDbContext = builder =>
    {
        builder.UseSqlServer(connectionString,sql => sql.MigrationsAssembly(migrationsAssembly));
    };
})
        .AddOperationalStore(options =>
{
    options.ConfigureDbContext = builder =>
    {
        builder.UseSqlServer(connectionString,sql => sql.MigrationsAssembly(migrationsAssembly));
    };
})
.AddAspNetIdentity<ApplicationUser>();

2、添加数据库迁移

add-migration InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
add-migration InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb

自动迁移、播种数据

SeedData.EnsureSeedData(connectionString);
SeedData.EnsureSeedAspNetAccountData(connectionString);//Identity
    public class SeedData
    {
        public static void EnsureSeedData(string connectionString)
        {
            var migtationAssembly = typeof(SeedData).Assembly.GetName().Name;
            var service = new ServiceCollection();
            service.AddConfigurationDbContext(options =>
            {
                options.ConfigureDbContext = db => db.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migtationAssembly));
            });
            service.AddOperationalDbContext(options =>
            {
                options.ConfigureDbContext = db => db.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migtationAssembly));
            });
            service.AddDbContext<AspNetAccountDbContext>(options =>
            {
                options.UseSqlServer(connectionString);
            });

            var serviceProvider = service.BuildServiceProvider();

            using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                //临时
                scope.ServiceProvider.GetService<PersistedGrantDbContext>().Database.Migrate();
                //配置
                var context = scope.ServiceProvider.GetService<ConfigurationDbContext>();
                context.Database.Migrate();
                EnsureSeedData(context);
                
                    
            }
        }

        private static void EnsureSeedData(IConfigurationDbContext context)
        {
            if (!context.Clients.Any())
            {
                foreach (var client in Config.Clients)
                    context.Clients.Add(client.ToEntity());
                context.SaveChanges();
            }
            if (!context.ApiResources.Any())
            {
                foreach (var api in Config.Apis)
                    context.ApiResources.Add(api.ToEntity());
                context.SaveChanges();
            }

            if (!context.IdentityResources.Any())
            {
                foreach (var id in Config.IdentityResources)
                    context.IdentityResources.Add(id.ToEntity());
                context.SaveChanges();
            }
        }

        public static void EnsureSeedAspNetAccountData(string connectionString)
        {
            var services = new ServiceCollection();
            services.AddLogging();
            services.AddDbContext<AspNetAccountDbContext>(options =>
            {
                options.UseSqlServer(connectionString);
            });

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<AspNetAccountDbContext>()
                .AddDefaultTokenProviders();

            services.Configure<IdentityOptions>(options =>
            {
                options.Password.RequireDigit = false;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 1;
                options.Password.RequireLowercase = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
            });

            using (var serviceProvider = services.BuildServiceProvider())
            {
                using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
                {
                    var context = scope.ServiceProvider.GetService<AspNetAccountDbContext>();
                    context.Database.Migrate();

                    var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();

                    var user = userManager.FindByNameAsync("fan").Result;
                    if (user == null)
                    {
                        user = new ApplicationUser
                        {
                            UserName = "fan",
                            Email = "410577910@qq.com",
                            
                        };
                        var result = userManager.CreateAsync(user, "123456").Result;
                        if (!result.Succeeded)
                        {
                            throw new Exception(result.Errors.First().Description);
                        }
                        result = userManager.AddClaimsAsync(user, new Claim[] {
                        new Claim(JwtClaimTypes.Name, "fan"),
                        new Claim(JwtClaimTypes.GivenName, "fan"),
                        new Claim(JwtClaimTypes.FamilyName, "fan"),
                        new Claim(JwtClaimTypes.Email, "410577910@qq.com"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://fan.com"),
                        new Claim(JwtClaimTypes.Address, @"{ '城市': '北京', '邮政编码': '10000' }",
                            IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json)
                    }).Result;

                        if (!result.Succeeded)
                        {
                            throw new Exception(result.Errors.First().Description);
                        }
                    }
                    // 为了代码简单一点,删除其他用户数据
                }
            }
        }
    }
posted @ 2022-05-14 11:45  .Neterr  阅读(324)  评论(0编辑  收藏  举报