查看原文
其他

.NET Core微服务之基于IdentityServer建立授权与验证服务

DotNet 2019-08-03

(点击上方蓝字,可快速关注我们)


来源:Edison Chou

cnblogs.com/edisonchou/p/identityserver4


一、IdentityServer的预备知识


要学习IdentityServer,事先得了解一下基于Token的验证体系,这是一个庞大的主题,涉及到Token,OAuth&OpenID,JWT,协议规范等。



重点关注一下上面这张图(来自solenovex的文章),对于一个User(已注册)来说,他会首先向Authorization Server表明自己的身份(比如输入用户名和密码),然后Authorization Server为其发放了一个token,而这个token就好比是把家里的钥匙配了一把(clone)新的,此后该User就可以访问API请求获取Orders(订单)数据了。


当然,实际中可能Authorization Server和API Server不在同一个区域内,它们可能只能遥望对方。此外,User还可以基于这个token去访问第三方服务,第三方服务会使用这个API来访问API Server,向其提供token比提供username&password要安全得多。


二、IdentityServer极简介绍


IdentityServer4(这里只使用版本号为4)是一个基于OpenID Connect和OAuth 2.0的针对ASP.NET Core 2.0的框架。


IdentityServer是将规范兼容的OpenID Connect和OAuth 2.0终结点添加到任意ASP.NET Core应用程序的中间件。


通常,你构建(或重新使用)包含登录和注销页面的应用程序,IdentityServer中间件会向其添加必要的协议头,以便客户端应用程序可以使用这些标准协议与其对话。



我们可以用IdentityServer来做啥?


(1)身份验证服务=>官方认证的OpenID Connect实现


(2)单点登录/注销(SSO)


(3)访问受控的API=>为不同的客户提供访问API的令牌,比如:MVC网站、SPA、Mobile App等


(4)等等等......


三、Started:第一个AuthorizationServer


1.1 建立一个ASP.NET Core空Web项目


建立ASP.NET Core项目,使用Empty空模板。


为了更好地查看日志信息,同时考虑到IISExpress启动起来真的很慢,修改lanuchSettings.json文件如下:


{

  "profiles": {

    "Manulife.DNC.MSAD.IdentityServer4Test": {

      "commandName": "Project",

      "launchBrowser": false,

      "environmentVariables": {

        "ASPNETCORE_ENVIRONMENT": "Development"

      },

      "applicationUrl": "http://localhost:5000/"

    }

  }

}


1.2 安装并配置IdentityServer4


Step1、首先安装IdentityServer4:


NuGet>Install-Package IdentityServer4


Step2、配置ASP.NET Core管道,即修改Configure方法


public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

    if (env.IsDevelopment())

    {

        app.UseDeveloperExceptionPage();

    }

    app.UseIdentityServer();

}


Step3、为了要把IdentityServer注册到容器中,需要对其进行配置,而这个配置中要包含三个信息:


(1)哪些API可以使用这个AuthorizationServer


(2)哪些Client可以使用这个AuthorizationServer


(3)哪些User可以被这个AuthrizationServer识别并授权


这里为了快速演示,我们写一个基于内存的静态类来快速填充上面这些信息(实际中,可以持久化在数据库中通过EF等ORM获取,也可以通过Redis获取):


public class InMemoryConfiguration

{

    public static IConfiguration Configuration { get; set; }

    /// <summary>

    /// Define which APIs will use this IdentityServer

    /// </summary>

    /// <returns></returns>

    public static IEnumerable<ApiResource> GetApiResources()

    {

        return new[]

        {

            new ApiResource("clientservice", "CAS Client Service"),

            new ApiResource("productservice", "CAS Product Service"),

            new ApiResource("agentservice", "CAS Agent Service")

        };

    }


    /// <summary>

    /// Define which Apps will use thie IdentityServer

    /// </summary>

    /// <returns></returns>

    public static IEnumerable<Client> GetClients()

    {

        return new[]

        {

            new Client

            {

                ClientId = "client.api.service",

                ClientSecrets = new [] { new Secret("clientsecret".Sha256()) },

                AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,

                AllowedScopes = new [] { "clientservice" }

            },

            new Client

            {

                ClientId = "product.api.service",

                ClientSecrets = new [] { new Secret("productsecret".Sha256()) },

                AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,

                AllowedScopes = new [] { "clientservice", "productservice" }

            },

            new Client

            {

                ClientId = "agent.api.service",

                ClientSecrets = new [] { new Secret("agentsecret".Sha256()) },

                AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,

                AllowedScopes = new [] { "agentservice", "clientservice", "productservice" }

            }

        };

    }


    /// <summary>

    /// Define which uses will use this IdentityServer

    /// </summary>

    /// <returns></returns>

    public static IEnumerable<TestUser> GetUsers()

    {

        return new[]

        {

            new TestUser

            {

                SubjectId = "10001",

                Username = "edison@hotmail.com",

                Password = "edisonpassword"

            },

            new TestUser

            {

                SubjectId = "10002",

                Username = "andy@hotmail.com",

                Password = "andypassword"

            },

            new TestUser

            {

                SubjectId = "10003",

                Username = "leo@hotmail.com",

                Password = "leopassword"

            }

        };

    }

}


Step4、对于Token签名需要一对公钥和私钥,不过IdentityServer为开发者提供了一个AddDeveloperSigningCredential()方法,它会帮我们搞定这个事,并默认存到硬盘中。当切换到生产环境时,还是得使用正儿八经的证书,更换为使用AddSigningCredential()方法。


public void ConfigureServices(IServiceCollection services)

{

    InMemoryConfiguration.Configuration = this.Configuration;

    services.AddIdentityServer()

        .AddDeveloperSigningCredential()

        .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())

        .AddInMemoryClients(InMemoryConfiguration.GetClients())

        .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());

}


1.3 获取你心心念念的Token


Step1、启动刚刚我们建立的AuthorizationServer程序,这里我们绑定的是5000端口。


Step2、启动Postman/SoapUI等API测试工具,通过向HTTP Body中填写数据发起POST请求:




Step3、发送一个错误的数据,看看返回的是啥?(这里输入了一个不在定义列表中的client_id)



Step4、查看控制台的日志信息:表示获取Token的这个请求成功了,日志中client_secret和password都是不会直接明文显示的。



Step5、IdentityServer中我们设置这几个API Service的Grant_Type是ResourceOwnerPasswordAndClientCredentials(点击这里了解=>资源拥有者密码凭据许可),因此我们还可以使用ClientCredentials(点击这里了解=>客户端凭据许可),如下所示:



Step6、再次查看控制台日志信息:这次没有关于User相关的任何信息显示了。



Step7、基本的开发结束,对于开发阶段,我们使用IdentityServer为开发者提供的临时证书即可,但是后面仍然需要生成一些正儿八经的证书。这里我们通过OpenSSL来生成,首先去官网下载一个,这里使用的是Win64_1.1版本。打开Powershell或者CMD,输入以下命令:


cmd>openssl req -newkey rsa:2048 -nodes -keyout cas.clientservice.key -x509 -days 365 -out cas.clientservice.cer


下面将生成的证书和Key封装成一个文件,以便IdentityServer可以使用它们去正确地签名tokens


cmd>openssl pkcs12 -export -in cas.clientservice.cer -inkey cas.clientservice.key -out cas.clientservice.pfx


中途会提示让你输入Export Password,这个password后面会用到,记住它。最终导出后的结果如下图所示:



这里我将其放到了项目结构文件夹中,并设置这个pfx文件为“如果较新则复制”,确保可以在最后生成的目录里边。现在就可以修改一下ConfigureServices()方法了:


public void ConfigureServices(IServiceCollection services)

{

    var basePath = PlatformServices.Default.Application.ApplicationBasePath;

    InMemoryConfiguration.Configuration = this.Configuration;


    services.AddIdentityServer()

        //.AddDeveloperSigningCredential()

        .AddSigningCredential(new X509Certificate2(Path.Combine(basePath,

            Configuration["Certificates:CerPath"]),

            Configuration["Certificates:Password"]))

      .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())

      .AddInMemoryClients(InMemoryConfiguration.GetClients())

    .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());

}


这里我将证书的路径和导出密码都写到了配置文件中:


{

  "Certificates": {

    "CerPath": "certificate\\cas.clientservice.pfx",

    "Password": "manulife"

  }


好,配置正儿八经的证书这一步骤Over。


四、IdentityServer QuickStart-UI


4.1 关于QuickStart UI


IdentityServer为我们提供了一套UI以便使我们能够快速地开发具有基本功能的认证/授权界面,我们可以去这个地址:https://github.com/IdentityServer/IdentityServer4.Quickstart.UI/tree/release 下载,并将其复制到我们的项目目录中。



复制完成后,我们的项目结构如下图所示:



4.2 修改DI方法


(1)使用MVC与静态文件(由于wwwroot下有很多静态资源文件)


public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

    if (env.IsDevelopment())

    {

        app.UseDeveloperExceptionPage();

    }


    app.UseIdentityServer();

    // for QuickStart-UI

    app.UseStaticFiles();

    app.UseMvcWithDefaultRoute();

}


(2)注册MVC


public void ConfigureServices(IServiceCollection services)

{

    var basePath = PlatformServices.Default.Application.ApplicationBasePath;

    InMemoryConfiguration.Configuration = this.Configuration;


    services.AddIdentityServer()

        //.AddDeveloperSigningCredential()

        .AddSigningCredential(new X509Certificate2(Path.Combine(basePath,

            Configuration["Certificates:CerPath"]),

            Configuration["Certificates:Password"]))

      .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())

      .AddInMemoryClients(InMemoryConfiguration.GetClients())

      .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());

    // for QuickStart-UI

    services.AddMvc();

}


4.3 Run  


(1)首页(这里由于我已经登录,所以这里会把我的账号显示了出来)



(2)Logout页,刚刚说到我已经实现Login了,所以我这里Logout一下



(3)Login页:这里只能识别我们在之前配置的静态User列表中那些User



登录之后,显示:"You have not given access to any applications",表示我们还没有给他授予访问任何API或网站模块的权限。后续我们会创建API和MVC网站来演示如何对其进行授权和访问。



五、小结


本篇主要简单的介绍了IdentityServer以及如何基于IdentityServer建立一个基本的AuthorizationServer,如何获取Token,以及集成QuickStart UI实现基本的界面展示。


后续还会创建API和MVC网站,来和IdentityServer进行集成,以演示如何对User授予访问API和MVC网站的访问权限。


示例代码


https://github.com/EdisonChou/EDC.IdentityServer4.Demo


看完本文有收获?请转发分享给更多人

关注「DotNet」,提升.Net技能 

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存