IdentityServer4结合Mysql前面写的示例中 IdeneityServ 使用的是内存缓存的存储方式 所有的配置都写在 Config cs 里 在实际应用中 应该使用数据库存储方式 方便随时配置 如添加新的用户 资源 客户端 也可以节省服务器内存 本文从三个方面来实现 IdentityServ 结合 Mysql 实现数据库存储方式 分别是客户端及资源数据 令牌及授权码数据以及用户数据 一 准备内容 1 准备 MySql 数据库服务器 新建一个空的数据库 2 IdentityServ 需要安装以下几个程序包 1 2
前面写的示例中,IdeneityServer使用的是内存缓存的存储方式,所有的配置都写在Config.cs里。在实际应用中,应该使用数据库存储方式,方便随时配置,如添加新的用户、资源、客户端,也可以节省服务器内存。
本文从三个方面来实现IdentityServer4结合Mysql实现数据库存储方式,分别是客户端及资源数据、令牌及授权码数据以及用户数据。
一,准备内容
1,准备MySql数据库服务器,新建一个空的数据库
2,IdentityServer需要安装以下几个程序包。
1
2
3
IdentityServer4.EntityFramework
Microsoft.EntityFrameworkCore.Tools
Pomelo.EntityFrameworkCore.MySql(也可以用MySql官方程序包:MySql.Data.EntityFrameworkCore)
3,appsettings.json添加数据库连接字符串
1
2
3
4
5
{
"ConnectionStrings": {
"MySqlDbConnectString": "server=IP;userid=mysqlUserName;pwd=user's password;database=database name;connectiontimeout=30;Pooling=true;Max Pool Size=300; Min Pool Size=5;"
}
}
二,客户端及资源的数据库存储
前面我们使用AddInMemory的方式加载配置数据
1
2
3
AddInMemoryIdentityResources(Config.GetIdentityResources())
AddInMemoryApiResources(Config.GetApis())
AddInMemoryClients(Config.GetClients())
把这三行代码注释掉,以下代码替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var connection = Configuration.GetConnectionString("MySqlDbConnectString");
var builder = services.AddIdentityServer()
//身份信息资源
//.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddConfigurationStore(opt =>
{
opt.ConfigureDbContext = context =>
{
context.UseMySql(connection, sql =>
{
sql.MigrationsAssembly("IdentityServer");
});
};
})
.AddTestUsers(Config.GetUsers());}
要从数据库查询配置数据,肯定得添加相应的数据表,把IdentityServer项目设为启动项目,打开程序包管理器控制台,设置控制台默认项目为IdentityServer,在控制台输入以下指令
1
2
3
4
PM> add-migration ConfigDbContext -c ConfigurationDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
To undo this action, use Remove-Migration.
PM> update-database
Done.
-c ConfigurationDbContext是指定当前的数据库上下文。ConfigurationDbContext定义在命名空间: IdentityServer4.EntityFramework.DbContexts,和昨们使用CodeFirst一样,定义了客户端及资源的数据表及表关联。add-migration生成迁移变动记录,update-datase更新数据库到最新状态。
但是现在数据库中的客户端和资源数据都是为空的,需要把Config的配置数据添加到数据库,可以在Start.cs中添加方法进行数据库初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService ().CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService ();
if (!context.Clients.Any())
{
foreach (var client in Config.GetClients())
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
}
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.GetIdentityResources())
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
if (!context.ApiResources.Any())
{
foreach (var resource in Config.GetApis())
{
context.ApiResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
}
}
说明:利用app.ApplicationServices.GetService ().CreateScope创建一个新的服务作用域与其他作用域隔离开,不影响其它作用域的上下文释放,操作实体更新数据库和我们平时用的一样。运行程序后,发现数据库已经有数据了
运行IdentityServer,IdentityMvc,IdentityApi三个程序进行测试
二,令牌和授权码的数据库存储
利用IIdentityServerBuilder的扩写方法AddOperationalStore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//客户端及资源数据库存储配置
.AddConfigurationStore(opt =>
{
opt.ConfigureDbContext = context =>
{
context.UseMySql(connection, sql =>
{
sql.MigrationsAssembly("IdentityServer");
});
};
})
//令牌及授权码数据库存储配置
.AddOperationalStore(opt =>
{
opt.ConfigureDbContext = context =>
{
context.UseMySql(connection, sql =>
{
sql.MigrationsAssembly("IdentityServer");
});
};
opt.EnableTokenCleanup = true;
opt.TokenCleanupInterval = 30;
})
.AddTestUsers(Config.GetUsers());
同样的,需要添加用来存储的数据表
1
2
3
4
PM> add-migration OperationContext -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/OperationDb
Multiple startup projects set.
PM> update-database -c PersistedGrantDbContext
Done.
运行IdentityServer,IdentityMvc,IdentityApi三个程序进行测试
三,用户的数据库存储
IdentityServer4为IIdentityServerBuilder提供了支持客户端和资源数据库存储的AddConfigurationStore方法,支持令牌和授权码数据库存储的AddOperationalStore,但没有提供用户数据库存储的方法。我想是因为客户端、资源、令牌、授权码的存储结构是强制规定的,而用户不是,每个人定义用户的资源、角色、权限、基础信息的存储结构等各不相同。不过没有关系,可以自己仿AddConfigurationStore写一个支持用户数据库存储的AddUserStore方法.
在IdentityServer项目新建一个名为IdentityUserStore的文件夹,创建四个类
IdentityServer.IdentityUserStore.IdentityUser:用户实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class IdentityUser
{
[Key]
[Required]
public string SubjectId { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
public string ProviderName { get; set; }
public string ProviderSubjectId { get; set; }
public bool IsActive { get; set; } public ICollection IdentityUserClaims { get; set; }
}
public class IdentityUserClaim
{
[Key]
public string ClaimId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Value { get; set; }
[Required]
public string UserSubjectId { get; set; }
[ForeignKey("UserSubjectId")]
public virtual IdentityUser IdentityUser { get; set; }
}
IdentityServer.IdentityUserStore.UserStoreDbContext:用于定义数据库操作上下文,作用和前面使用过的ConfigurationDbContext、PersistedGrantDbContext一样
1
2
3
4
5
6
7
8
9
public class UserStoreDbContext:DbContext
{
public UserStoreDbContext(DbContextOptions opt) : base(opt)
{
}
public DbSet IdentityUser { get; set; }
public DbSet IdentityUserClaim { get; set; }
}
IdentityServer.IdentityUserStore.UserStore:用于查询用户信息和验证登录密码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class UserStore
{
private readonly UserStoreDbContext _dbContext;
public UserStore(UserStoreDbContext dbContext)
{
_dbContext = dbContext;
}
///
/// 根据SubjectID查询用户信息
///
/// 用户id
///
public IdentityUser FindBySubjectId(string subjectId) {
return _dbContext.Set ().Where(r => r.SubjectId.Equals(subjectId)).Include(r => r.IdentityUserClaims).SingleOrDefault();
}
///
/// 根据用户名查询用户
///
/// 用户
///
public IdentityUser FindByUsername(string username)
{
return _dbContext.Set ().Where(r => r.Username.Equals(username)).Include(r => r.IdentityUserClaims).SingleOrDefault();
}
///
/// 验证登录密码
///
///
///
///
public bool ValidateCredentials(string username, string password)
{
password = Config.MD5Str(password);
var user = _dbContext.Set ().Where(r => r.Username.Equals(username)
&&r.Password.Equals(password)).Include(r => r.IdentityUserClaims).SingleOrDefault();
return user != null;
}
}
IdentityServer.IdentityUserStore.IIdentityServerBuilderUserStoreExtensions:对IIdentityServerBuilder的扩写,添加UserStoreDbContext、UserStoreDbContext的服务依赖。
1
2
3
4
5
6
7
8
9
public static class IIdentityServerBuilderUserStoreExtensions
{
public static IIdentityServerBuilder AddUserStore(this IIdentityServerBuilder builder, Action userStoreOptions = null)
{
builder.Services.AddDbContext (userStoreOptions);
builder.Services.AddTransient ();
return builder;
}
}
在Startup.ConfigureServices方法中以下面代码替换AddTestUser(Config.GetUsers)
1
2
3
4
5
6
7
8
//.AddTestUsers(Config.GetUsers())
.AddUserStore(opt =>
{
opt.UseMySql(connection, sql =>
{
sql.MigrationsAssembly("IdentityServer");
});
})
添加用户存储数据表,在程序包管理器中输入以下指令
1
2
3
4
5
6
PM> add-migration UserStoreContext -c UserStoreDbContext -o Data/Migrations/IdentityServer/UserDb
PM> update-database -c UserStoreDbContext
Multiple startup projects set.
Using project 'src\IdentityServer' as the startup project.
Done.
PM>
在数据库初始方法中添加Config类中的用户数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService ().CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService ();
var userContext = serviceScope.ServiceProvider.GetRequiredService ();
//添加config中的客户端数据到数据库
if (!context.Clients.Any())
{
foreach (var client in Config.GetClients())
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
}
//添加config中的IdentityResources数据到数据库
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.GetIdentityResources())
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
//添加config中的ApiResources数据到数据库
if (!context.ApiResources.Any())
{
foreach (var resource in Config.GetApis())
{
context.ApiResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
//添加config中的Users数据到数据库
if (!userContext.IdentityUser.Any())
{
int index = 0;
foreach(var user in Config.GetUsers())
{
IdentityUser iuser = new IdentityUser()
{
IsActive = user.IsActive,
Password = user.Password,
ProviderName = user.ProviderName,
ProviderSubjectId = user.ProviderSubjectId,
SubjectId = user.SubjectId,
Username = user.Username,
IdentityUserClaims = user.Claims.Select(r => new IdentityUserClaim()
{
ClaimId = (index++).ToString(),
Name = r.Type,
Value = r.Value
}).ToList()
};
userContext.IdentityUser.Add(iuser);
}
userContext.SaveChanges();
}
}
}
可以看到数据库已经有用户数据了
最后一步,登录时改用上面新建的UserStore类来验证登录密码以及查询用户信息,并添加用户Claim。
AccountController构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//private readonly TestUserStore _users; 这里改成了UserStore
private readonly UserStore _users;
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore;
private readonly IAuthenticationSchemeProvider _schemeProvider;
private readonly IEventService _events;
public AccountController(
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IAuthenticationSchemeProvider schemeProvider,
IEventService events,
//TestUserStore _users=null,这里改成了UserStore
UserStore users)
{
// _users = User ?? new TestUserStore(Config.GetUsers()); 这里改成了UserStore
_users = users;
_interaction = interaction;
_clientStore = clientStore;
_schemeProvider = schemeProvider;
_events = events;
}
Task Login(LoginInputModel model, string button),登录时把数据库中的IdentityUserClaims数据转化为Claim数据传入登录重载方法。
1
2
List claims = user.IdentityUserClaims.Select(r => new Claim(r.Name,r.Value) ).ToList();
await HttpContext.SignInAsync(user.SubjectId, user.Username, props, claims.ToArray());
运行IdentityServer,IdentityApi,IdentityMvc三个项目后进行登录授权-获取access_token-访问api
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/199133.html 原文链接:https://javaforall.net