eShopOnContainers 知多少[3]:Identity microservice

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

引言

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

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

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

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

而本节所讲的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的认证的差异以下所示:架构

Cookie-Based Auth VS Token-Based Auth

架构模式

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

项目结构以下所示:
Identity.API 项目结构

核心技术选型:

  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();
}

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

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

最后

本文从业务和技术上对本服务进行剖析,介绍了其技术选型,并紧接着简要介绍了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实现统一验证与受权

相关文章
相关标签/搜索