使用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

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/318601.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

再不学习我们就out了

前不久我们组来了个Graduate Developer&#xff0c;刚毕业&#xff0c;经验不多&#xff0c;有一次闹了个乌龙&#xff0c;把Stage数据库直接删掉了……好在Azure有备份&#xff0c;不然就算Stage没有重要数据&#xff0c;也得花点时间重建&#xff0c;其他的开发、测试工作都得…

矩阵快速幂一篇通

文章目录概述快速幂解析代码矩阵运算定义加法乘法单位矩阵一、斐波拉契&#xff08;基础模板&#xff09;题目描述解析代码二、行为方案&#xff08;实际应用&#xff09;题目描述解析代码三、矩阵求和&#xff08;子矩阵作为矩阵元素&#xff09;题目描述解析代码四、最短路径…

玉米田(加加强版)【插头dp】

前言 水解警告&#xff0c;数据水勉强卡过的 正题 题目大意 n∗mn*mn∗m的网格里面有些格子被禁止&#xff0c;现在求选取若干个不相邻的格子的方案数。 1≤n≤120,1≤m≤211\leq n\leq 120,1\leq m\leq 211≤n≤120,1≤m≤21 解题思路 听说是插头dpdpdp然后想了一下觉得比插…

牛客题霸 [将字符串转化为整数] C++题解/答案

牛客题霸 [将字符串转化为整数] C题解/答案 题目描述 实现函数 atoi 。函数的功能为将字符串转化为整数 提示&#xff1a;仔细思考所有可能的输入情况。这个问题没有给出输入的限制&#xff0c;你需要自己考虑所有可能的情况。 题解&#xff1a; 题目很简单&#xff0c;但是…

用ABP入门DDD

前言ABP框架一直以来都是用DDD&#xff08;领域驱动设计&#xff09;作为宣传点之一。但是用过ABP的人都知道&#xff0c;ABP并不是一个严格遵循DDD的开发框架&#xff0c;又或者说&#xff0c;它并没有完整实现DDD的所有概念。但是反过来说&#xff0c;认真学过DDD的人会发现&…

多重背包的二进制优化(ybtoj-宝物筛选)

文章目录题目描述解析朴素算法代码二进制优化代码thanks for reading!题目描述 解析 朴素算法 首先考虑朴素算法 把数量为num的物体拆成num个子物体 其价值与重量是原物体的1&#xff0c;2&#xff0c;3…num倍 然后当成独立的物体求就行了 注意应该先枚举重量&#xff0c;再…

基于.NET Standard的分布式自增ID算法--Snowflake

概述本篇文章主要讲述分布式ID生成算法中最出名的Snowflake算法。搞.NET开发的&#xff0c;数据库主键最常见的就是int类型的自增主键和GUID类型的uniqueidentifier。那么为何还要引入snowflake呢&#xff1f;INT自增主键自增主键是解决主键生成的最简单方案&#xff0c;它有如…

2021“MINIEYE杯”中国大学生算法设计超级联赛(4)Display Substring(后缀数组+二分)

Display Substring #include<bits/stdc.h> using namespace std; typedef long long ll; // sa[i]: 排名是i位的是第几个后缀 // rk[i]: 第i个后缀的排名是多少 // height[i]: sa[i]与sa[i-1] const int N100010; char s[N]; int rk[N],sa[N],cnt[N],height[N]; int x[N]…

领域驱动设计,让程序员心中有码(二)

引子&#xff0c;软件工程没有银弹上一篇博文领域驱动设计&#xff0c;让程序员心中有码&#xff0c;抛出了一个问题&#xff0c;领域驱动设计真的是万能的良方吗&#xff1f;对于这个问题&#xff0c;大家的答案无疑是一致的&#xff0c;作为一种非常受软件行业欢迎的软件思想…

邮局-[IOI2000](四边形不等式)

概要 四边形不等式的核心在于缩小最优转移的范围 题目描述 传送门 解析 这道题说是不等式&#xff0c;但其实也可以感性理解 &#xff08;其实就是不想证明&#xff09; 定义pl[i][k]: i到n的村庄建造k座邮局时&#xff0c;第一座管辖的范围是i-pl[i][k] (也就是最优决策…

.NET Core实战项目之CMS 第九章 设计篇-白话架构设计

前面两篇文章给大家介绍了我们实战的CMS系统的数据库设计&#xff0c;源码也已经上传到服务器上了。今天我们就好聊聊架构设计&#xff0c;在开始之前先给大家分享一下这几天我一直在听的《从零开始学架构》里面关于架构设计的定义以及架构设计的三大原则&#xff0c;希望能对大…

今日头条Marketing API小工具(.Net Core版本)

前言由于工作原因&#xff0c;需要用到今日头条的Marketing API做一些广告投放的定制化开发。然后看现在网上也没多少关于头条Marketing API的文章&#xff0c;于是便就有了该篇文章。头条Marketing API主页地址&#xff1a;https://ad.toutiao.com/openapi/index.html。头条Ma…

.NET Core实战项目之CMS 第十章 设计篇-系统开发框架设计

这两天比较忙&#xff0c;周末也在加班&#xff0c;所以更新的就慢了一点&#xff0c;不过没关系&#xff0c;今天我们就进行千呼万唤的系统开发框架的设计。不知道上篇关于架构设计的文章大家有没有阅读&#xff0c;如果阅读后相信一定对架构设计有了更近一部的理解&#xff0…

分析现有 WPF / Windows Forms 程序能否顺利迁移到 .NET Core 3.0

今年五月的 Build 大会上&#xff0c;微软说 .NET Core 3.0 将带来 WPF / Windows Forms 这些桌面应用的支持。当然&#xff0c;是通过 Windows 兼容包&#xff08;Windows Compatibility Pack&#xff09;实现的。为了提前检查你的程序是否能在未来跑在 .NET Core 3.0 上&…

ML.NET 0.8特性简介

本周.NET生态圈内的更新源源不断&#xff0c;除了.NET Core 2.2&#xff0c;ASP.NET Core 2.2和Entity Framework Core 2.2之外&#xff0c;ML.NET 0.8也一并登上舞台。新的推荐场景ML.NET使用基于矩阵分解(Matrix Factorization)和场感知分解机(Field-aware Factorization Mac…

F-Lucky Pascal Triangle(Lucas+数位dp)

F-Lucky Pascal Triangle issue是fw题解 下面代码TLE了&#xff0c;但是此题数位dp的思想非常值得学习 Lucas的过程相当于把n,mn,mn,m在p进制下的每一位拿出来做组合数 Lucas(n,m,p)∏(nkmk)modp\text{Lucas}(n,m,p)\prod \dbinom {n_k}{m_k} \bmod pLucas(n,m,p)∏(mk​nk​…

树的合并(ybtoj-树上dp)

文章目录题目描述前言解析代码thanks for reading&#xff01;题目描述 前言 全网唯一AC&#xff01;&#xff01;&#xff01; 妙啊 而且还是完全自己想出来的做法 开心 &#xff08;APIO还是没白听&#xff09; 但是思路出来后代码实现十分坎坷 建两个图分别dfs3次那个地方…

.net core中的高效动态内存管理方案

.net core在新增的System.Buffers中引入了一大堆高效内存管理的类&#xff0c;如span和memory、内存池。本文今天这里介绍一个高效动态内存访问方案。ReadOnlySequenceSegment<T>在我们读取数据的过程&#xff0c;很多时候会出现如下场景&#xff1a;不知道数据实际大小一…

.net core 上 K8S(三)Yaml文件运行.netcore程序

正文上一章我们通过kubectl run简单运行了一个.netcore网站&#xff0c;但实际的开发中&#xff0c;我们都是通过yaml来实现的。1.编写yaml文件关于yaml文件的格式在此就不多描述了&#xff0c;不熟悉的可以去网上搜一下示例。2.运行yamlkubectl create -f netcore.yaml 我们可…

Jozky模板

文章目录字符串处理后缀数组manacherhashKMP最大最小表达法数论约瑟夫环欧拉函数莫比乌斯反演逆序对归并排序求逆序对素数线性筛欧几里得与扩展欧几里得欧几里得算法&#xff1a;扩展欧几里得算法&#xff1a;逆元扩展欧几里得费马小定理欧拉定理递推求逆元__int128高精度运算唯…