Identity Server4学习系列三

1、简介

Identity Server4学习系列一Identity Server4学习系列二之令牌(Token)的概念的基础上,了解了Identity Server4的由来,以及令牌的相关知识,本文开始实战,实现Identity Server4基本的功能。

2、前提

本文基于.Net Core2.1和Indetity Server4 2.3.0,令牌处理包采用IdentityServer4.AccessTokenValidation 2.7.0

3、实战一Identity Server4服务端配置

(1)、项目结构

(2)、站点入口文件Program.cs类

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        /// <summary>
        /// 设置当前项目的服务器宿主,Windows下默认为IIS
        /// 设置启动类为Startup类
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }

注意:如果时Linux环境,这里在这里可以切换站点的宿主服务器

(3)、Startup启动类(配置Identity Server4的相关参数和MVC的相关参数,并注入到管道模型中)

    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            //优雅的链式编程
            //注入Identity Server4服务到DI容器中
            services.AddIdentityServer()
           //注入临时签名凭据到DI容器,后期可用签名证书的密钥替换,用于生成零时密钥
           .AddDeveloperSigningCredential()
           //注入需要受Identity Server4保护的Api资源添注入到DI容器中 -内存级别
           .AddInMemoryApiResources(Apis.GetApiResources())
           //注入需要访问受Identity Server4保护的Api资源的客户端注入到DI容器中 -内存级别
           .AddInMemoryClients(ThirdClients.GetClients());

            //注入基本的MVC服务
            services.AddMvcCore()
            //注入MVC的认证服务,对应控制器的Authorize特性
           .AddAuthorization()
           //注入MVC格式化程序,对应JsonResult等等的格式化操作,主要用于控制器返回值的格式化操作
           .AddJsonFormatters();

            //注入身份认证服务,设置Bearer为默认方案
            services.AddAuthentication("Bearer")
            //注入并配置Bearer为默认方案的基本参数
            .AddIdentityServerAuthentication(options =>
            {
                //设置令牌的发布者
                options.Authority = "http://localhost:5000";
                //设置Https
                options.RequireHttpsMetadata = false;
                //需要认证的api资源名称
                options.ApiName = "api1";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //如果当前时开发者模式
            if (env.IsDevelopment())
            {
                //从管道中捕获同步和异步System.Exception实例并生成HTML错误响应。
                app.UseDeveloperExceptionPage();
            }

            //将IdentityServer 4服务注入到管道模型中(对应上面的IdentityServer 4服务的配置)
            app.UseIdentityServer();

            //将认证服务通过Microsoft.AspNetCore.Authentication.AuthenticationMiddleware中间件
            //注入到管道模型中(对应上面认证服务的配置)
            app.UseAuthentication();

            //将mvc添加到Microsoft.AspNetCore.Builder.IApplicationBuilder请求执行中(对应上的MVC配置)
            app.UseMvc();
        }
    }

(4)、配置第三方客户端能成功在认证模式下能成功访问Api资源的资本参数

    /// <summary>
    /// 配置可以访问IdentityServer4 保护的Api资源模型的第三方客户端
    /// 配置客户端访问的密钥
    /// 配置
    /// </summary>
    public class ThirdClients
    {
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>()
            {
                new Client()
                {
                    //客户端的唯一Id,客户端需要指定该ClientId才能访问
                     ClientId = $"client",

                    //no interactive user, use the clientid/secret for authentication
                    //使用客户端密钥进行认证
                    AllowedGrantTypes = GrantTypes.ClientCredentials,

                    // 认证密钥
                    ClientSecrets =
                    {
                        //用Sha256对"secret"进行加密,客户端必须使用secret密钥才能成功访问
                        new Secret("secret".Sha256())
                    },

                    // scopes that client has access to
                    //如果客户端的密钥认证成功,限定该密钥可以访问的Api范围
                    AllowedScopes = { "api1" }
                }
            };
        }
    }

注意ClientId(分配给不同客户端的Id),对应的客户端调用时传递过来的ClientId必须一致,否则客户端发起调用时汇报这个错:

密钥也是一样,密钥是我们分配给客户端的,客户端只有给对了我们分配给它的ClientId和密钥的同时,才能访问对应的api,所以如果你的密钥不对,客户端发起调用时也会报这个错:

(5)、配置受保护的Api资源模型

    public class Apis
    {
        //ApiResource -IdentityServer4.Models下的Api资源模型
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>()
            {
                //Api资源模型
                new ApiResource("api1", "My API")
            };
        }
    }

注意ApiRescourse的名称必须和Client的AllowedScopes属性对应,否则客户端调用时会报下面这个错:

(6)、验证服务端是否配置成功

开启站点,浏览器输入http://localhost:5000/.well-known/openid-configuration,等到如下返回报文说明服务部署成功:

{   //令牌签发者,对应StartUp中的Identity Server4中的认证配置
    "issuer":"http://localhost:5000",    //jwt令牌处理地址
    "jwks_uri":"http://localhost:5000/.well-known/openid-configuration/jwks",
    "authorization_endpoint":"http://localhost:5000/connect/authorize",
    "token_endpoint":"http://localhost:5000/connect/token",
    "userinfo_endpoint":"http://localhost:5000/connect/userinfo",
    "end_session_endpoint":"http://localhost:5000/connect/endsession",
    "check_session_iframe":"http://localhost:5000/connect/checksession",
    "revocation_endpoint":"http://localhost:5000/connect/revocation",
    "introspection_endpoint":"http://localhost:5000/connect/introspect",
    "device_authorization_endpoint":"http://localhost:5000/connect/deviceauthorization",
    "frontchannel_logout_supported":true,
    "frontchannel_logout_session_supported":true,
    "backchannel_logout_supported":true,
    "backchannel_logout_session_supported":true,
    "scopes_supported":[
        "api1",
        "offline_access"
    ],
    "claims_supported":[

    ],
    "grant_types_supported":[
        "authorization_code",
        "client_credentials",
        "refresh_token",
        "implicit",
        "urn:ietf:params:oauth:grant-type:device_code"
    ],
    "response_types_supported":[
        "code",
        "token",
        "id_token",
        "id_token token",
        "code id_token",
        "code token",
        "code id_token token"
    ],
    "response_modes_supported":[
        "form_post",
        "query",
        "fragment"
    ],
    "token_endpoint_auth_methods_supported":[
        "client_secret_basic",
        "client_secret_post"
    ],
    "subject_types_supported":[
        "public"
    ],
    "id_token_signing_alg_values_supported":[
        "RS256"
    ],
    "code_challenge_methods_supported":[
        "plain",
        "S256"
    ]
}

参数含义,自行了解

3、实战一客户端调用受Identity Server4保护的Api资源

(1)、前提

客户端必须安装IdentityModel 3.10.4包

(2)、调用代码如下:

    class Program
    {
        static void Main(string[] args)
        {
            Request();
            Console.ReadKey();
        }

        async static void Request()
        {
            //请求Identity Server4服务
            var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return;
            }
            //生成Identity Server4授权的客户端,通过指定对应的ClientId和密钥(secret)
            var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

            if (tokenResponse.IsError)
            {
                Console.WriteLine(tokenResponse.Error);
                return;
            }
            Console.WriteLine(tokenResponse.Json);

            //通过Identity Server4的认证过后,拿到AccessToken
            var client = new HttpClient();
            client.SetBearerToken(tokenResponse.AccessToken);
            var response = await client.GetAsync("http://localhost:5000/identity");
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
            }
            else
            {
                //认证成功,输出Identity控制器的返回值
                var content = await response.Content.ReadAsStringAsync();
                Console.WriteLine(JArray.Parse(content));
            }
        }
    }

得到如下报文:

同时查看Identity Server4服务端的输出:

第一步:客户端传入在Indetity Server4中注册过的分配给该客户端的ClientId和密钥,拿到AccessToken

第二步:第一次请求目标控制器,并把AcessToken带过去

第三步:验证Token是否有效

第四步:Token有效,开始调用Identity控制器方法,并拿到响应值

大致的流程如上.

原文地址:https://www.cnblogs.com/GreenLeaves/p/10123697.html

时间: 2024-08-06 14:04:06

Identity Server4学习系列三的相关文章

Identity Server4学习系列四之用户名密码获得访问令牌

1.简介 Identity Server4支持用户名密码模式,允许调用客户端使用用户名密码来获得访问Api资源(遵循Auth 2.0协议)的Access Token,MS可能考虑兼容老的系统,实现了这个功能,但是不建议这么做. 2.实战一服务端配置 接着Identity Server4学习系列三的基础上,直接扩展里面的项目代码,让服务端同时支持密钥认证和用户名密码认证 第一步:扩展ThirdClients类,如下: /// <summary> /// 配置可以访问IdentityServer4

C# Redis学习系列三:Redis配置主从

Redis配置主从 主IP :端口      192.168.0.103 6666 从IP:端口       192.168.0.108 3333 配置从库 (1)安装服务: redis-server --service-install --service-name redisService6666 --port 6666 (2)启动进程: redis-server --service-start --service-name redisService6666 (3)连接redis:redis-

NMock学习系列(三)--- NMock在DDD领域驱动的单元测试中的应用

介绍 领域驱动设计涵盖的知识点比较多,其中代码的架构.设计.编写基本上只占到其中的很小一部分,其它的大部分讲解的是需求的获取方式.项目的管理方式等知识.本篇就是针对这一小部分的知识点位来展开的.所以本篇的学习前提是只需要了解DDD的架构分层即可. 应用场景 DDD领域驱动设计中一旦领域驱动层模型建立完毕,就会产生出数据库持久化的接口即仓储的接口供其它层来做具体实现,所以要想建立领域层的单元测试,就必须实现这些仓储接口或者模拟出这些接口实现.我们可以采用NMock来进行模拟仓储的实现.下面开始学习

Vue学习系列(三)——基本指令

前言 在上一篇中,我们已经对组件有了更加进一步的认识,从组件的创建构造器到组件的组成,进而到组件的使用,.从组件的基本使用.组件属性,以及自定义事件实现父子通讯和巧妙运用插槽slot分发内容,进一步的认识到组件在Vue中的核心地位. 而今天,我们将对vue中的基本指令进行了解汇总,指令在Vue中是个很重要的功能,在Vue项目中是必不可少的.根据官网的介绍,指令 (Directives) 是带有 v- 前缀的特殊属性.指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM.

web多终端开发学习系列(三)--- 基于bootstrap的表格插件bootstrap-table

基于网页管理系统的开发大多数功能只是对数据的显示与操作,对于数据的显示一般都是通过table表格来显示,所以管理系统的开发很有必要引入表格插件,对于sencha touch等商业化框架,都有自己自带的表格控件,而对于bootstrap需要引入第三方的表格插件,这里我学习下bootstrap table. 介绍 bootstrap table是基于bootstrap框架的一个表格插件,官网地址是http://wenzhixin.net.cn/p/bootstrap-table/docs/index

Quartz.NET学习系列(三)--- Cron触发器

Cron触发器是使用Cron表达式来配置任务的时间的. Cron表达式介绍 Cron表达式总共有7个值,其中一个可选,具体如下(摘自官方文档): 值 是否必须 允许的值范围 允许的字符 Seconds YES 0-59 , - * / Minutes YES 0-59 , - * / Hours YES 0-23 , - * / Day of month YES 1-31 , - * ? / L W Month YES 1-12 or JAN-DEC , - * / Day of week YE

Spring学习系列(三) 通过Java代码装配Bean

上面梳理了通过注解来隐式的完成了组件的扫描和自动装配,下面来学习下如何通过显式的配置的装配bean 二.通过Java类装配bean 在前面定义了HelloWorldConfig类,并使用@ComponentScan和@Configuration注解,@Configuration注解表明了这个类是一个java配置类,该类用在获取Spring应用上下文时,告诉Spring创建bean的细节,通过@ComponentScan,我们启用了Spring的自动组件扫描,现在就让我们来看如果通过java类来显

ABP架构学习系列三:手工搭建ABP框架

由于公司的项目才接触到ABP这个框架,当时就觉得高大上,什么IOC.AOP.ddd各种专业词汇让人激情 澎湃,但在使用过程中碰到了许多坑,可能也许是没有去看源码导致的,但工作确实没有那么多时间让人去慢慢研究.很久之前想手动搭建这个框架了,但是各种理由,你懂的.但是要在技术上得到大的提升就得静的下心去研究,学到大神的思想和精髓,运用到实际中去,才能去体验更开阔的天地. 本文以创建博客为思路,一步步构建整个项目,在摸索中进步,也希望能够帮助到有需要的人. 一.基础架构 第一部分主要是搭建好整个项目的

mybatis学习系列三(部分)

1 forearch_oracle下批量保存(47) oracle批量插入 不支持values(),(),()方式 1.多个insert放在begin-end里面 begin insert into myemployeee(id,last_name,email,gender,dept_id) values (myemployeee_seq.nextval,#{emp.lastName,jdbcType=VARCHAR},#{emp.email,jdbcType=VARCHAR}, insert