【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
用于配置数据,如clients
,resources
和scopes
。 - 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);
}
}
// 为了代码简单一点,删除其他用户数据
}
}
}
}