首先感谢晓晨Master和EdisonChou的审稿!也感谢正在阅读的您!

引言

通常,服务所公开的资源和 API 必须仅限受信任的特定用户和客户端访问。那进行 API 级别信任决策的第一步就是身份认证——确定用户身份是否可靠。

在微服务场景中,身份认证通常统一处理。一般有两种实现形式:

  1. 基于API 网关中心化认证:要求客户端必须都通过网关访问微服务。(这就要求提供一种安全机制来认证请求是来自于网关。)

  2. 基于安全令牌服务(STS)认证:所有的客户端先从STS获取令牌,然后请求时携带令牌完成认证。

而本节所讲的Identity microservice就是使用第二种身份认证方式。

服务简介

Identity microservice 主要用于统一的身份认证和授权,为其他服务提供支撑。

提到认证,大家最熟悉不过的当属Cookie认证了,它也是目前使用最多的认证方式。但Cookie认证也有其局限性:不支持跨域、移动端不友好等。而从当前的架构来看,需要支持移动端、Web端、微服务间的交叉认证授权,所以传统的基于Cookie的本地认证方案就行不通了。我们就需要使用远程认证的方式来提供统一的认证授权机制。

而远程认证方式当属:OAuth2.0和OpenID Connect了。借助OAuth2.0和OpenID Connect即可实现类似下图的认证体系:

而如何实现呢,借助:

  1. ASP.NET Core Identity
  2. IdentityServer4

基于Cookie的认证和基于Token的认证的差别如下所示:

架构模式

该微服务作为支撑服务,并没有选择复杂的架构模式,使用了MVC单层架构,使用EF Core ORM框架用于数据持久化,SQL Server数据库。使用Autofac IOC框架替换了默认依赖注入框架。

项目结构如下所示:

核心技术选型:

  1. MVC单层架构
  2. EF Core
  3. ASP.NET Core Identity
  4. IdentityServer4
  5. SQL Server 数据库
  6. Autofac

PS:对ASP.NET Core Identity、IdentityServer4以及OAuth2.0不了解的,请先行阅读文末参考资料补课!!!

下面就着重讲解ASP.NET Core Identity和IdentityServer4在本服务中的使用。

ASP.NET Core Identity && IdentityServer4简介

ASP.NET Core Identity用于构建ASP.NET Core Web应用程序的成员资格系统,包括成员资格,登录和用户数据(包括登录信息、角色和声明)。

ASP.NET Core Identity封装了User、Role、Claim等身份信息,便于我们快速完成登录功能的实现,并且支持第三方登录(Google、Facebook、QQ、Weixin等,支持开箱即用[第三方身份提供商列表]),以及双重验证,同时内置支持Bearer 认证(令牌认证)。

虽然ASP.NET Core Identity已经完成了绝大多数的功能,且支持第三方登录(第三方为其用户颁发令牌),但若要为本地用户颁发令牌,则需要自己实现令牌的颁发和验证逻辑。换句话说,我们需要自行实现OpenId Connect协议。

OpenID Connect 1.0 是基于OAuth 2.0协议之上的简单身份层,它允许客户端根据授权服务器的认证结果最终确认终端用户的身份,以及获取基本的用户信息。

而IdentityServer4就是为ASP.NET Core量身定制的实现了OpenId Connect和OAuth2.0协议的认证授权中间件。IdentityServer4在ASP.NET Core Identity的基础上,提供令牌的颁发验证等。

认证流程简介

在ASP.NET Core中使用的是基于申明(Claim)的认证,而什么是申明(Cliam)呢?

Claim 是关于一个人或组织的某个主题的陈述,比如:一个人的名称,角色,个人喜好,种族,特权,社团,能力等等。它本质上就是一个键值对,是一种非常通用的保存用户信息的方式,可以很容易的将认证和授权分离开来,前者用来表示用户是/不是什么,后者用来表示用户能/不能做什么。在认证阶段我们通过用户信息获取到用户的Claims,而授权便是对这些的Claims的验证,如:是否拥有Admin的角色,姓名是否叫XXX等等。

认证主要与以下几个核心对象打交道:

  1. Claim(身份信息)
  2. ClaimsIdentity(身份证)
  3. ClaimsPrincipal (身份证持有者)
  4. AuthorizationToken (授权令牌)
  5. IAuthenticationScheme(认证方案)
  6. IAuthenticationHandler(与认证方案对应的认证处理器)
  7. IAuthenticationService (向外提供统一的认证服务接口)

那其认证流程是怎样的呢?

用户打开登录界面,输入用户名密码先行登录,服务端先行校验用户名密码是否有效,有效则返回用户实例(User),这时进入认证准备阶段,根据用户实例携带的身份信息(Claim),创建身份证(ClaimsIdentity),然后将身份证交给身份证持有者(ClaimsPrincipal)持有。接下来进入真正的认证阶段,根据配置的认证方案(IAuthenticationScheme),使用相对应的认证处理器(IAuthenticationHandler)进行认证 。认证成功后发放授权令牌(AuthorizationToken)。该授权令牌包含后续授权阶段需要的全部信息。

授权流程简介

授权就是对于用户身份信息(Claims)的验证,,授权又分以下几种种:

  1. 基于Role的授权
  2. 基于Scheme的授权
  3. 基于Policy的授权

授权主要与以下几个核心对象打交道:

  1. IAuthorizationRequirement(授权条件)
  2. IAuthorizationService(授权服务)
  3. AuthorizationPolicy(授权策略)
  4. IAuthorizationHandler (授权处理器)
  5. AuthorizationResult(授权结果)

那授权流程是怎样的呢?

当收到授权请求后,由授权服务(IAuthorizationService)根据资源上指定的授权策略(AuthorizationPolicy)中包含的授权条件(IAuthorizationRequirement),找到相对应的授权处理器(IAuthorizationHandler )来判断授权令牌中包含的身份信息是否满足授权条件,并返回授权结果。

中间件集成

简单了解了下认证和授权流程后,我们来了解Identity microservice是如何集成相关中间件的。

1. 首先是映射自定义扩展的User和Role

 // 映射自定义的User,Role
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()//配置使用EF持久化存储
.AddDefaultTokenProviders();//配置默认的TokenProvider用于变更密码和修改email时生成Token

2. 配置IdentityServer服务

// Adds IdentityServer
services.AddIdentityServer(x =>
{
x.IssuerUri = "null";
x.Authentication.CookieLifetime = TimeSpan.FromHours(2);
})
.AddSigningCredential(Certificate.Get())
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
})
.Services.AddTransient<IProfileService, ProfileService>();

IdentityServer默认直接在内存中存储配置数据(客户端和资源)和操作数据(令牌,代码和和用户的授权信息consents)。这显然在生产环境是不合适的,如果服务所在主机宕机,那么内存中的数据就会丢失,所以有必要持久化到数据库。

其中AddConfigurationStoreAddOperationalStore扩展方法就是用来来指定配置数据和操作数据基于EF进行持久化。

3. 添加IdentityServer中间件

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// .....
// Adds IdentityServer
app.UseIdentityServer();
}

4. 预置种子数据

从已知的体系结构来说,我们需要预置Client和Resource:

  1. Client
public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl)
{
return new List<Client>
{
// SPA OpenId Client Client(Implicit)
new Client
// Xamarin Client(Hybrid)
new Client
// MVC Client(Hybrid)
new Client
// MVC TEST Client(Hybrid)
new Client
// Locations Swagger UI(Implicit)
new Client
// Marketing Swagger UI(Implicit)
new Client
// Basket Swagger UI(Implicit)
new Client
// Ordering Swagger UI(Implicit)
new Client
// Mobile Shopping Aggregattor Swagger UI(Implicit)
new Client
// Web Shopping Aggregattor Swagger UI(Implicit)
new Client
};
}
  1. IdentityResources
public static IEnumerable<IdentityResource> GetResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
  1. ApiResources
public static IEnumerable<ApiResource> GetApis()
{
return new List<ApiResource>
{
new ApiResource("orders", "Orders Service"),
new ApiResource("basket", "Basket Service"),
new ApiResource("marketing", "Marketing Service"),
new ApiResource("locations", "Locations Service"),
new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"),
new ApiResource("webshoppingagg", "Web Shopping Aggregator"),
new ApiResource("orders.signalrhub", "Ordering Signalr Hub")
};
}

5. 迁移数据库上下文

下面就把提前在代码预置的种子数据迁移到数据库中,我们如何做呢?IdentityServer为配置数据和操作数据分别定义了DBContext用于持久化,配置数据对应ConfigurationDbContext,操作数据对应PersistedGrantDbContext。代码如下所示:

public static void Main(string[] args)
{
BuildWebHost(args)
.MigrateDbContext<PersistedGrantDbContext>((_, __) => { })//迁移操作数据库
.MigrateDbContext<ApplicationDbContext>((context, services) =>
{
var env = services.GetService<IHostingEnvironment>();
var logger = services.GetService<ILogger<ApplicationDbContextSeed>>();
var settings = services.GetService<IOptions<AppSettings>>(); new ApplicationDbContextSeed()
.SeedAsync(context, env, logger, settings)
.Wait();
})//迁移用户数据库
.MigrateDbContext<ConfigurationDbContext>((context,services)=>
{
var configuration = services.GetService<IConfiguration>(); new ConfigurationDbContextSeed()
.SeedAsync(context, configuration)
.Wait();
})//迁移配置数据库
.Run();
}

至此,本服务的核心代码已解析完毕。

最终的生成的数据库如下图所示:

最后

本文从业务和技术上对本服务进行剖析,介绍了其技术选型,并紧接着简要介绍了ASP.NET Core Identity和IdentityServer4,最后分析源码,一步步揭开其神秘的面纱。至于客户端和其他微服务服务如何使用Identity microservice进行认证和授权,我将在后续文章再行讲解。

如果对ASP.NET Core Idenity和IdentityServer4不太了解,建议大家博客园阅读雨夜朦胧晓晨MasterSavorboard

的博客进行系统学习后,再重读本文,相信你对Identity microservice的实现机制豁然开朗。

参考资料

雨夜朦胧 -- ASP.NET Core 认证与授权:初识认证/授权

Savorboard -- ASP.NET Core 之 Identity 入门(一)

晓晨Master -- IdentityServer(14)- 通过EntityFramework Core持久化配置和操作数据

IdentityServer4 知多少

OAuth2.0 知多少

.NET Core微服务之基于Ocelot+IdentityServer实现统一验证与授权

eShopOnContainers 知多少[3]:Identity microservice的更多相关文章

  1. eShopOnContainers 知多少[7]:Basket microservice

    引言 Basket microservice(购物车微服务)主要用于处理购物车的业务逻辑,包括: 购物车商品的CRUD 订阅商品价格更新事件,进行购物车商品同步处理 购物车结算事件发布 订阅订单成功创 ...

  2. eShopOnContainers 知多少[1]:总体概览

    引言 在微服务大行其道的今天,Java阵营的Spring Boot.Spring Cloud.Dubbo微服务框架可谓是风水水起,也不得不感慨Java的生态圈的火爆.反观国内.NET阵营,微服务却不愠 ...

  3. eShopOnContainers 知多少[2]:Run起来

    环境准备 Win10(开启Hyper-V) .NET Core SDK Docker for Windows VS2017 or VS Code Git SQL Server Management S ...

  4. eShopOnContainers 知多少[8]:Ordering microservice

    1. 引言 Ordering microservice(订单微服务)就是处理订单的了,它与前面讲到的几个微服务相比要复杂的多.主要涉及以下业务逻辑: 订单的创建.取消.支付.发货 库存的扣减 2. 架 ...

  5. eShopOnContainers 知多少[4]:Catalog microservice

    引言 Catalog microservice(目录微服务)维护着所有产品信息,包括库存.价格.所以该微服务的核心业务为: 产品信息的维护 库存的更新 价格的维护 架构模式 如上图所示,本微服务采用简 ...

  6. eShopOnContainers 知多少[10]:部署到 K8S | AKS

    1. 引言 断断续续,感觉这个系列又要半途而废了.趁着假期,赶紧再更一篇,介绍下如何将eShopOnContainers部署到K8S上,进而实现大家常说的微服务上云. 2. 先了解下 Helm 读过我 ...

  7. eShopOnContainers 知多少[9]:Ocelot gateways

    引言 客户端与微服务的通信问题永远是一个绕不开的问题,对于小型微服务应用,客户端与微服务可以使用直连的方式进行通信,但对于对于大型的微服务应用我们将不得不面对以下问题: 如何降低客户端到后台的请求数量 ...

  8. eShopOnContainers 知多少[5]:EventBus With RabbitMQ

    1. 引言 事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉.事件总线是对发布-订阅模式的一种实现.它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需 ...

  9. eShopOnContainers 知多少[6]:持久化事件日志

    1. 引言 事件总线解决了微服务间如何基于集成事件进行异步通信的问题.然而只有事件总线正常运行,微服务之间基于事件的通信才得以运转. 而现实情况是,总有这样或那样的问题,导致事件总线不稳定或不可用,比 ...

随机推荐

  1. CMD命令锦集

    虽然随着计算机产业的发展,Windows 操作系统的应用越来越广泛,DOS 面临着被淘汰的命运,但是因为它运行安全.稳定,有的用户还在使用,所以一般Windows 的各种版本都与其兼容,用户可以在Wi ...

  2. Android 框架练成 教你打造高效的图片加载框架

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41874561,本文出自:[张鸿洋的博客] 1.概述 优秀的图片加载框架不要太多, ...

  3. hibernate入门-基本配置及简单的crud操作

    框架来说主要是需要写大量的配置文件,hibernate相比mybatis来说更强大,移植性更好: 1.类和数据库的映射配置:配置文件命名一般--类名.hbm.xml (user.hbm.xml),与实 ...

  4. 通过pycharm的Database设置进行数据库的可视化

    pycharm是一个很好的IDE,它还有一个功能直接通过这个IDE连接数据库,然后对数据库进行相关的操作,这样我们可以不用navicat for mysql这样的可视化工具了.   输入账号密码数据库 ...

  5. HTTP状态码的详细解释,供参考

    HTTP状态码详解 常用对照表 状态码 含义 100 客户端应当继续发送请求.这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝.客户端应当继续发送请求的剩余部分,或者如果请求已经 ...

  6. IntelliJ IDEA(十) :常用操作

    IDEA功能详细,快捷键繁多,但是实际开发时不是所有都能用上,如果我们熟悉一些常用的也足够满足我们日常开发了,多的也只是提高我们的B格. 1.自定义主题 IDEA默认的主题有三款,分别是Intelli ...

  7. 安卓----Spinner

    <?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android=" ...

  8. 【莫比乌斯反演】BZOJ3309 DZY Loves Math

    Description 对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b, ...

  9. BZOJ_1778_[Usaco2010 Hol]Dotp 驱逐猪猡_概率DP+高斯消元

    BZOJ_1778_[Usaco2010 Hol]Dotp 驱逐猪猡_概率DP+高斯消元 题意: 奶牛们建立了一个随机化的臭气炸弹来驱逐猪猡.猪猡的文明包含1到N (2 <= N <= 3 ...

  10. Django之META与前后端交互

    Django之META与前后端交互 1 提交表单之GET 前端提交数据与发送 1)提交表单数据 2)提交JSON数据 后端的数据接收与响应 1)接收GET请求数据 2)接收POST请求数据 3)响应请 ...