前言
在前几篇的学习中,我们定义的 ApiResource、ApiScope、IdentityResource 都是存储在内存中的,通过 AddInMemoryApiScopes(Startup.GetApiScopes())、AddInMemoryIdentityResources(Startup.GetIdentityResources())、 AddInMemoryApiResources(Startup.GetApiResources()) 的方式注入到 IDS4 的服务中。本篇我们学习如何使用数据库或其他持久化方法存储和读取 Resource 。
IdentityServer4 中 Resource 概念
在 IdentityServer4 中一共有3种资源类型:IdentityResource、ApiResource、ApiScope,定义这些资源的名称必须是唯一的,不可重复。
IdentityResource
身份资源,表示关于用户的声明,如用户ID、显示名、电子邮件地址等。
ApiResource
对 ApiScope 高层分组(归类)。当 API 资源变大时,使用 ApiScope 作用域列表可能不可行。通常需要引入某种名称空间来组织作用域名称,可能还希望将它们分组在一起,并获得一些更高级的构造,如访问令牌中的受众声明。如:多个资源应该支持相同的作用域名称,但是有时您显式地希望将一个作用域隔离到某个资源。 在IdentityServer中,ApiResource类允许一些额外的组织
ApiScope
资源作用域。
IResourceStore 接口
IDS4 为开发者定义了一个 IResourceStore 接口,实现该接口即可做自己的 Resource 持久化存储和读取。
CustomerResourceStore 实现
新建 CustomerResourceStore 类,实现 IResourceStore 接口,写我们自己的资源查找规则。
为了简单,笔者使用的 Resource 全部是已赋值好的内容,笔友可以通过数据库或其他持久化介质获取 Resource。
FindIdentityResourcesByScopeNameAsync
1 2 3 4 5 6 7 | /// <summary> /// 身份资源 /// </summary> private readonly List<IdentityResource> identityResources = new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /// <summary> /// 通过 名称 查找符合要求的 IdentityResources /// </summary> /// <param name="scopeNames"></param> /// <returns></returns> public async Task<IEnumerable<IdentityResource>> FindIdentityResourcesByScopeNameAsync(IEnumerable< string > scopeNames) { var result = new List<IdentityResource>(); foreach ( var name in scopeNames) { if (identityResources.Count(t => t.Name == name) > 0) { result.Add(identityResources.First(t => t.Name == name)); } } return result; } |
FindApiResourcesByNameAsync
1 2 3 4 5 6 7 8 9 10 11 | /// <summary> /// ApiResource 资源 /// </summary> private readonly List<ApiResource> apiResources = new List<ApiResource> { new ApiResource( "admin" , "用户管理" ){ Scopes = { "user.list" , "user.delete" } }, new ApiResource( "article" , "文章管理" ){ Scopes = { "article.list" , "article.delete" } }, }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /// <summary> /// 通过 名称 查找符合要求的 ApiResources /// </summary> /// <param name="apiResourceNames"></param> /// <returns></returns> public async Task<IEnumerable<ApiResource>> FindApiResourcesByNameAsync(IEnumerable< string > apiResourceNames) { var result = new List<ApiResource>(); foreach ( var name in apiResourceNames) { if (apiResources.Count(t=>t.Name== name) > 0) { result.Add(apiResources.First(t => t.Name == name)); } } return result; } |
FindApiScopesByNameAsync
1 2 3 4 5 6 7 8 9 10 | /// <summary> /// ApiScope 资源 /// </summary> private readonly List<ApiScope> apiScopeResources = new List<ApiScope> { new ApiScope( "article.list" , "文章-查看" ), new ApiScope( "article.delete" , "文章-删除" ), new ApiScope( "user.list" , "用户-查看" ), new ApiScope( "user.delete" , "部门-删除" ), }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /// <summary> /// 通过 名称 查找符合要求的 ApiScopes /// </summary> /// <param name="scopeNames"></param> /// <returns></returns> public async Task<IEnumerable<ApiScope>> FindApiScopesByNameAsync(IEnumerable< string > scopeNames) { var result = new List<ApiScope>(); foreach ( var name in scopeNames) { if (apiScopeResources.Count(t => t.Name == name) > 0) { result.Add(apiScopeResources.First(t => t.Name == name)); } } return result; } |
FindApiResourcesByScopeNameAsync
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /// <summary> /// 通过 作用域名称 查找符合要求的 ApiResources /// </summary> /// <param name="scopeNames"></param> /// <returns></returns> public async Task<IEnumerable<ApiResource>> FindApiResourcesByScopeNameAsync(IEnumerable< string > scopeNames) { var result = new List<ApiResource>(); foreach ( var name in scopeNames) { foreach ( var apiResource in apiResources) { if (apiResource.Scopes.Contains(name) && result.Count(t=>t.Name == name) ==0) { result.Add(apiResource); } } } return result; } |
GetAllResourcesAsync
1 2 3 4 5 6 7 8 | /// <summary> /// 返回所有的资源 /// </summary> /// <returns></returns> public async Task<Resources> GetAllResourcesAsync() { return new Resources(identityResources, apiResources, apiScopeResources); } |
修改 Client 的 AllowedScopes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | new Client { ClientId = "client2" , // 用户名 密码 模式 AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, // 用于认证的密码 ClientSecrets = { new Secret( "secret" .Sha256()) }, // 客户端有权访问的范围(Scopes) AllowedScopes = { "openid" , "profile" , "article.list" , "article.delete" , "user.list" , "user.delete" } } |
删除注入的内存资源服务
PostMan 测试