使用Dapper持久化IdentityServer4
最近研究dotnet core,微软将IdentityServer4作为推荐的服务授权和验证的组件,其独立性特别适合微服务或者分布式的服务扩展验证,所以非常受广大dotnet开发人员的青睐.默认的IdentityServer4默认使用内存对象的验证和授权,而在IdentityServer的官方推荐只有Entity Framework core的集成,默认也只有SQL Server的实例,如果想要使用MySQL等其他数据库,Google了好多或多或少都遇到了很多坑,本人也尝试了N(N>4)小时,最终还是放弃使用EF Core,改用比较透明化的Dapper来实现持久层.最终的项目地址如下:https://github.com/DarinHan/IdentityServer4.Dapper
关于该项目的使用方法参考项目的说明,有一份MySQL的Demo示例,如果使用SQL Server,也有类似的方法供使用.
下面就具体实现原理给大家介绍下细节,方便大家理解IdentityServer4的内部原理.
在研究了IdentityServer4.EntityFramework的源代码后,知道要实现IdentityServer中内置对象的Client,Apiresource,Identityresource,PersistedGrant的持久化,主要要实现3个Store,即项目中的ClientStore,ResourceStore,PersistedGrantStore.
从字面意思来看,这三个Store承担着商店的角色,提供Client,Resource以及PersistedGrant的查询等功能.此外我们知道如果需要查询这三个对象,前提是我们得先保存到数据库中,所以对于Client对象我们需要实现查询(Store的角色),新增和修改的功能,这样如果我们在管理后台中才能通过新增和修改的功能动态维护这些对象.为了不污染Store在IdentityServer中的定义,我们引入IProvider接口的概念,分别对应四个接口(区分API和Identity)如下
using IdentityServer4.Models;
using System.Collections.Generic;
namespace IdentityServer4.Dapper.Interfaces
{
public interface IClientProvider
{
Client FindClientById(string clientid);
void Add(Client client);
IEnumerable<string> QueryAllowedCorsOrigins();
}
}
using IdentityServer4.Models;
using System.Collections.Generic;
namespace IdentityServer4.Dapper.Interfaces
{
public interface IIdentityResourceProvider
{
IEnumerable<IdentityResource> FindIdentityResourcesByScope(IEnumerable<string> scopeNames);
IEnumerable<IdentityResource> FindIdentityResourcesAll();
void Add(IdentityResource identityResource);
IdentityResource FindIdentityResourcesByName(string name);
}
}
using IdentityServer4.Models;
using System.Collections.Generic;
namespace IdentityServer4.Dapper.Interfaces
{
public interface IApiResourceProvider
{
ApiResource FindApiResource(string name);
IEnumerable<ApiResource> FindApiResourcesByScope(IEnumerable<string> scopeNames);
IEnumerable<ApiResource> FindApiResourcesAll();
void Add(ApiResource apiResource);
}
}
using IdentityServer4.Models;
using System.Collections.Generic;
namespace IdentityServer4.Dapper.Interfaces
{
public interface IPersistedGrantProvider
{
IEnumerable<PersistedGrant> GetAll(string subjectId);
IEnumerable<PersistedGrant> GetAll(string subjectId, string clientId);
IEnumerable<PersistedGrant> GetAll(string subjectId, string clientId, string type);
PersistedGrant Get(string key);
void Add(PersistedGrant token);
void Update(PersistedGrant token);
void RemoveAll(string subjectId, string clientId);
void RemoveAll(string subjectId, string clientId, string type);
void Remove(string key);
void Store(PersistedGrant grant);
}
}
在我们得Store中通过的注入的方式使用接口对应的服务,并实现对应IStore对应的接口方法,比如ClientStore实现如下.
public class ClientStore : IClientStore
{
private readonly IClientProvider _clientDB;
private readonly ILogger<ClientStore> _logger;
public ClientStore(IClientProvider client, ILogger<ClientStore> logger)
{
_clientDB = client ?? throw new ArgumentNullException(nameof(client));
_logger = logger;
}
public Task<Client> FindClientByIdAsync(string clientId)
{
var client = _clientDB.FindClientById(clientId);
_logger.LogDebug("{clientId} found in database: {clientIdFound}", clientId, client != null);
return Task.FromResult<Client>(client);
}
}
在Identity这样,最终对应Client的读写操作都转移到了IClientProvider中.
Server4.Dapper.DefaultProviders命名空间下,我们提供了Iprovider的默认实现.实现方法使用了Dapper和AutoMapper实现了数据库操作,这里就不一一举例了.值得一说的是部分对象字段都是SQL的关键字,直接执行SQL会报错,我们使用了数据库中的列名保护的方法,具体实现方法在各个数据库实例项目中配置.比如在MySQL的实现中,我们配置保护字符为'`'.
public static class IdentityServerDapperExtensions
{
public static IIdentityServerBuilder AddMySQLProvider(this IIdentityServerBuilder builder, Action<DBProviderOptions> dbProviderOptionsAction = null)
{
//config mysql
var options = new DBProviderOptions();
options.DbProviderFactory = new MySqlClientFactory();
//get last insert id for insert actions
options.GetLastInsertID = "select last_insert_id();";
//config the ColumnName protect string, mysql using "`"
options.ColumnProtect = new System.Collections.Generic.Dictionary<string, string>();
options.ColumnProtect.Add("left", "`");
options.ColumnProtect.Add("right", "`");
//add singgleton
builder.Services.AddSingleton(options);
dbProviderOptionsAction?.Invoke(options);
return builder;
}
}
最终实现的IProvider使用扩展方法注入到容器中.
public static IIdentityServerBuilder AddConfigurationStore(this IIdentityServerBuilder builder, Action<ConfigurationStoreOptions> storeOptionsAction = null)
{
var options = new ConfigurationStoreOptions();
storeOptionsAction?.Invoke(options);
builder.Services.AddSingleton(options);
builder.Services.AddTransient<Interfaces.IClientProvider, DefaultProviders.DefaultClientProvider>();
builder.Services.AddTransient<Interfaces.IApiResourceProvider, DefaultProviders.DefaultApiResourceProvider>();
builder.Services.AddTransient<Interfaces.IIdentityResourceProvider, DefaultProviders.DefaultIdentityResourceProvider>();
builder.AddClientStore<ClientStore>();
builder.AddResourceStore<ResourceStore>();
builder.AddCorsPolicyService<CorsPolicyService>();
return builder;
}
在OperationStore方法中,需要将原来IdentityServer4中默认提供的InMemory的实例移除,再添加新的实例.
public static IIdentityServerBuilder AddOperationalStore(this IIdentityServerBuilder builder, Action<OperationalStoreOptions> storeOptionsAction = null)
{
builder.Services.AddSingleton<TokenCleanup>();
builder.Services.AddSingleton<IHostedService, TokenCleanupHost>();//auto clear expired tokens
builder.Services.AddTransient<Interfaces.IPersistedGrantProvider, DefaultProviders.DefaultPersistedGrantProvider>();
builder.Services.AddTransient<Interfaces.IPersistedGrantStoreClanup, DefaultProviders.DefaultPersistedGrantProvider>();
var storeOptions = new OperationalStoreOptions();
storeOptionsAction?.Invoke(storeOptions);
builder.Services.AddSingleton(storeOptions);
var memopersistedstore = builder.Services.FirstOrDefault(c => c.ServiceType == typeof(IPersistedGrantStore));
if (memopersistedstore != null)
{
builder.Services.Remove(memopersistedstore);
}
builder.Services.AddSingleton<IPersistedGrantStore, PersistedGrantStore>();
memopersistedstore = builder.Services.FirstOrDefault(c => c.ServiceType == typeof(IPersistedGrantStore));
return builder;
}
到此,基本的持久化改造已经完成了,当然在该项目中还实现了一个自动删除过期Token的服务,这个服务也是EFCore中实现的,基本上是把功能复制过来,具体细节稍有改造.
原文地址:https://blog.csdn.net/u013710468/article/details/81675747
.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com
使用Dapper持久化IdentityServer4相关推荐
- 【.NET Core项目实战-统一认证平台】第九章 授权篇-使用Dapper持久化IdentityServer4...
上篇文章介绍了IdentityServer4的源码分析的内容,让我们知道了IdentityServer4的一些运行原理,这篇将介绍如何使用dapper来持久化Identityserver4,让我们对I ...
- 【.NET Core项目实战-统一认证平台】第十四章 授权篇-自定义授权方式
上篇文章我介绍了如何强制令牌过期的实现,相信大家对IdentityServer4的验证流程有了更深的了解,本篇我将介绍如何使用自定义的授权方式集成老的业务系统验证,然后根据不同的客户端使用不同的认证方 ...
- .NET Core微服务之路:基于Ocelot的API网关实现--http/https协议篇
前言 最近一直在忙公司和私下的兼职,白天十个小时,晚上四个小时,感觉每天都是打了鸡血似的,精神满满的,连自己那已经学打酱油的娃都很少关心,也有很长一段时间没有更新博客了,特别抱歉,小伙伴们都等得想取关 ...
- IdentityServer4-前后端分离的授权验证(六)
上两节介绍完Hybrid模式在MVC下的使用,包括验证从数据获取的User和Claim对MVC的身份授权.本节将介绍Implicit模式在JavaScript应用程序中的使用,使用Node.js+Ex ...
- IdentityServer4-从数据库获取User登录并对Claims授权验证(五)
本节将在第四节基础上介绍如何实现IdentityServer4从数据库获取User进行验证,并对Claim进行权限设置. 一.新建Web API资源服务,命名为ResourceAPI (1)新建API ...
- 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程...
上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本篇就针对第一个思考问题详细的讲解下Ids4是如何生成acce ...
- IdentityServer4-MVC+Hybrid实现Claims授权验证(四)
上节IdentityServer4-客户端的授权模式原理分析(三)以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍Hybrid模式在MVC下的使用.且为实现IdentityServer4从数 ...
- IdentityServer4-客户端的授权模式原理分析(三)
在学习其他应用场景前,需要了解几个客户端的授权模式.首先了解下本节使用的几个名词 Resource Owner:资源拥有者,文中称"user": Client为第三方客户端: Au ...
- 【.NET Core项目实战-统一认证平台】第十一章 授权篇-密码授权模式
上篇文章介绍了基于Ids4客户端授权的原理及如何实现自定义的客户端授权,并配合网关实现了统一的授权异常返回值和权限配置等相关功能,本篇将介绍密码授权模式,从使用场景.源码剖析到具体实现详细讲解密码授权 ...
最新文章
- 2019计算机考研学硕,2019计算机考研选学硕、专硕、非全日制研究生哪个好?
- hadoop官方文档_hadoop体系简介
- python 怎么安装电脑摄像头模块_Python模块及安装
- python类型转换、数值操作
- Java基础知识:Java实现Map集合二级联动4
- 海贼王革命家—龙—实力到底如何?
- Andorid 反编译App
- 最高分数的学生姓名(信息学奥赛一本通-T1147)
- 浅谈Opencl四大模型之Platform model
- ERROR: Minions returned with non-zero exit code
- .Net转Java自学之路—基础巩固篇三十(JDBC)
- 蓝牙耳机续航比较好的推荐,音质最好的耳机盘点
- 三国群雄传ol服务器 修改,三国群英传OL DATA.PAK相关修改
- 中首清算:她被誉为中国第一美女,迷倒国际花花公子,今36岁仍单身
- 挂耳式蓝牙耳机哪家的好用,推荐几款实用的挂耳式耳机
- EUI-64生成IPv6地址
- python出现warning_Python warning警告出现的原因及忽略方法
- java 序列化 内存溢出_Gson序列化问题导致的内存溢出,tip:Background sticky concurrent mark sweep GC freed...
- GPRS联网过程简介
- Python知识体系-Python工具大全