IdentityServer4系列 | 客户端凭证模式

一、前言

从上一篇关于 快速搭建简易项目中,通过手动或者官方模板的方式简易的实现了我们的IdentityServer授权服务器搭建,并做了相应的配置和UI配置,实现了获取Token方式。

而其中我们也注意到了三点就是,有哪些用户(users)可以通过哪些客户端(clents)来访问我们的哪些API保护资源 (API)。

所以在这一篇中,我们将通过多种授权模式中的客户端凭证模式进行说明,主要针对介绍「IdentityServer」保护API的资源,客户端认证授权访问API资源。

二、初识

Client Credentials 客户端凭证模式:客户端(Client)请求授权服务器验证,通过验证就发access token,Client直接以已自己的名义去访问Resource server的一些受保护资源。

用户使用这个令牌访问资源服务器,当令牌失效时使用刷新令牌去换取新的令牌(刷新令牌有效时间大于访问令牌,刷新令牌的功能不做详细介绍)

这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。

2.1 适用范围

这种模式一般只用在服务端与服务端之间的认证

适用于没有前端的命令行应用,即在命令行请求令牌

认证服务器不提供像用户数据这样的重要资源,仅仅是有限的只读资源或者一些开放的API。例如使用了第三方的静态文件服务,如Google Storage或Amazon S3。这样,你的应用需要通过外部API调用并以应用本身而不是单个用户的身份来读取或修改这些资源。这样的场景就很适合使用客户端证书授权。

2.2 Client Credentials流程:

 +---------+                                  +---------------+|         |                                  |               ||         |>--(A)- Client Authentication --->| Authorization || Client  |                                  |     Server    ||         |<--(B)---- Access Token ---------<|               ||         |                                  |               |+---------+                                  +---------------+

「客户端凭据许可流程描述」

(A)客户端与授权服务器进行身份验证并向令牌端点请求访问令牌。

(B)授权服务器对客户端进行身份验证,如果有效,颁发访问令牌。

2.2.1 过程详解


访问令牌请求

参数是否必须含义
grant_type必需授权类型,值固定为“client_credentials”。
scope可选表示授权范围。

示例:客户端身份验证两种方式

1、Authorization: Bearer base64(resourcesServer:123) 

2、client_id(客户端标识),client_secret(客户端秘钥)。

POST /token HTTP/1.1
Host: authorization-server.comgrant_type=client_credentials
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

2.2.2 访问令牌响应

刷新令牌不应该包含在内。
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"Bearer",
"expires_in":3600,
"scope":"server"
}

三、实践

在示例实践中,我们将创建一个授权访问服务,定义一个API和要访问它的客户端,客户端通过「IdentityServer」上请求访问令牌,并使用它来访问API。

3.1 搭建 Authorization Server 服务

搭建认证授权服务

3.1.1 安装Nuget包

IdentityServer4 程序包

3.1.2 配置内容

建立配置内容文件Config.cs

public static class Config
{public static IEnumerable<IdentityResource> IdentityResources =>new IdentityResource[]{new IdentityResources.OpenId(),new IdentityResources.Profile(),};public static IEnumerable<ApiScope> ApiScopes =>new ApiScope[]{new ApiScope("client_scope1")};public static IEnumerable<ApiResource> ApiResources =>new ApiResource[]{new ApiResource("api1","api1"){Scopes={"client_scope1" }}};public static IEnumerable<Client> Clients =>new Client[]{// m2m client credentials flow clientnew Client{ClientId = "credentials_client",ClientName = "Client Credentials Client",AllowedGrantTypes = GrantTypes.ClientCredentials,ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256())    },AllowedScopes = { "client_scope1" }},};
}

3.1.3 注册服务

在startup.cs中ConfigureServices方法添加如下代码:

        public void ConfigureServices(IServiceCollection services){var builder = services.AddIdentityServer();// .AddTestUsers(TestUsers.Users);// in-memory, code configbuilder.AddInMemoryIdentityResources(Config.IdentityResources);builder.AddInMemoryApiScopes(Config.ApiScopes);builder.AddInMemoryApiResources(Config.ApiResources);builder.AddInMemoryClients(Config.Clients);// not recommended for production - you need to store your key material somewhere securebuilder.AddDeveloperSigningCredential();}

3.1.4 配置管道

在startup.cs中Configure方法添加如下代码:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseIdentityServer();app.UseEndpoints(endpoints =>{endpoints.MapGet("/", async context =>{await context.Response.WriteAsync("Hello World!");});});}

以上内容是快速搭建简易IdentityServer项目服务的方式,具体说明可以看上一篇的内容。

3.2 搭建API资源

实现对API资源进行保护

3.2.1 快速搭建一个API项目

3.2.2 安装Nuget包

IdentityServer4.AccessTokenValidation 包

3.2.3 注册服务

在startup.cs中ConfigureServices方法添加如下代码:

    public void ConfigureServices(IServiceCollection services){services.AddControllersWithViews();services.AddAuthorization();services.AddAuthentication("Bearer").AddIdentityServerAuthentication(options =>{options.Authority = "http://localhost:5001";options.RequireHttpsMetadata = false;options.ApiName = "api1";});}

AddAuthentication把Bearer配置成默认模式,将身份认证服务添加到DI中。

AddIdentityServerAuthentication把IdentityServer的access token添加到DI中,供身份认证服务使用。

3.2.4 配置管道

在startup.cs中Configure方法添加如下代码:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}    app.UseRouting();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapDefaultControllerRoute();});}

UseAuthentication将身份验证中间件添加到管道中;

UseAuthorization 将启动授权中间件添加到管道中,以便在每次调用主机时执行身份验证授权功能。

2.5 添加API资源接口

[Route("api/[Controller]")]
[ApiController]
public class IdentityController:ControllerBase
{[HttpGet("getUserClaims")][Authorize]public IActionResult GetUserClaims(){return new JsonResult(from c in User.Claims select new { c.Type, c.Value });}
}

在IdentityController 控制器中添加 [Authorize] , 在进行请求资源的时候,需进行认证授权通过后,才能进行访问。

3.3 搭建Client客户端

实现对API资源的访问和获取资源

3.3.1 搭建一个窗体程序

3.3.2 安装Nuget包

「IdentityModel」

3.3.3 获取令牌

客户端与授权服务器进行身份验证并向令牌端点请求访问令牌。授权服务器对客户端进行身份验证,如果有效,颁发访问令牌。

「IdentityModel」 包括用于发现 「IdentityServer」 各个终结点(EndPoint)的客户端库。

我们可以使用从 「IdentityServer」 元数据获取到的Token终结点请求令牌:

        private void getToken_Click(object sender, EventArgs e){var client = new HttpClient();var disco = client.GetDiscoveryDocumentAsync(this.txtIdentityServer.Text).Result;if (disco.IsError){this.tokenList.Text = disco.Error;return;}//请求tokentokenResponse = client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest{Address = disco.TokenEndpoint,ClientId =this.txtClientId.Text,ClientSecret = this.txtClientSecret.Text,Scope = this.txtApiScopes.Text}).Result;if (tokenResponse.IsError){this.tokenList.Text = disco.Error;return;}this.tokenList.Text = JsonConvert.SerializeObject(tokenResponse.Json);this.txtToken.Text = tokenResponse.AccessToken;}

3.3.4 调用API

要将Token发送到API,通常使用HTTP Authorization标头。这是使用SetBearerToken扩展方法完成。

    private void getApi_Click(object sender, EventArgs e){//调用认证apiif (string.IsNullOrEmpty(txtToken.Text)){MessageBox.Show("token值不能为空");return;}var apiClient = new HttpClient();//apiClient.SetBearerToken(tokenResponse.AccessToken);apiClient.SetBearerToken(this.txtToken.Text);var response = apiClient.GetAsync(this.txtApi.Text).Result;if (!response.IsSuccessStatusCode){this.resourceList.Text = response.StatusCode.ToString();}else{this.resourceList.Text = response.Content.ReadAsStringAsync().Result;}}

以上展示的代码有不明白的,可以看本篇项目源码,项目地址为 :

3.4 效果

3.4.1 项目测试

3.4.2 postman测试

四、问题

「注意,如果你的代码没问题,但是依然报错,比如“无效的scope”,“Audience validation failed”等问题。」

在3.1.x 到 4.x 的变更中,ApiResourceScope 正式独立出来为 ApiScope 对象,区别ApiResourceScope的关系, Scope 是属于ApiResource 的一个属性,可以包含多个Scope

所以在配置ApiResource、ApiScope、Clients中,我们有些地方需要注意:

在3.x版本中

 public static IEnumerable<ApiResource> GetApiResources(){return new[] { new ApiResource("api1", "api1") };}

改成4.x版本为

public static IEnumerable<ApiResource> ApiResources =>new ApiResource[]
{new ApiResource("api1","api1"){Scopes={"client_scope1" }}
};public static IEnumerable<ApiScope> ApiScopes =>new ApiScope[]
{new ApiScope("client_scope1")
};

因此,

这里比之前3.x版本多了一个添加ApiScopes的方法:

builder.AddInMemoryApiScopes(Config.ApiScopes);

因为接下来有要保护的API资源,所以需要添加一行:

builder.AddInMemoryApiResources(Config.ApiResources);
  1. 如果在4.x版本中,不添加ApiScopes方法的话,在获取token令牌的时候一直“无效的scope”等错误

  2. 在授权访问保护资源的时候,如果ApiResource中不添加Scopes, 会一直报Audience validation failed错误,得到401错误,所以在4.x版本中写法要不同于3.x版本

所以,需要注意的是4.x版本的ApiScope和ApiResource是分开配置的,然后在ApiResource中一定要添加Scopes。

五、总结

  1. 本篇主要以「客户端凭证模式」进行授权,我们通过创建一个认证授权访问服务,定义一个API和要访问它的客户端,客户端通过「IdentityServer」上请求访问令牌,并使用它来控制访问API。

  2. 在文中可能出现的问题,我们通过查找解决,以及前后版本之间的差异,并总结说明问题。

  3. 在后续会对其中的其他授权模式,数据库持久化问题,以及如何应用在API资源服务器中和配置在客户端中,会进一步说明。

  4. 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。

  5. 项目地址

    https://github.com/i3yuan/Yuan.IdentityServer4.Demo/tree/main/DiffAuthMode/ClientCredentials

六、附加

Client Authentication认证

client-credentials资料

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

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

相关文章

数据结构 - 链表

准备重启尘封一年的博客作为学习笔记&#xff0c;看看自己能坚持多久。 最近会记录做过的算法题&#xff0c;语言描述只用于会意&#xff0c;仅供参考。 文章目录0.从尾到头获取链表的值&#xff08;不是反转链表&#xff09;1.寻找/删除单链表倒数第k个节点3.寻找单链表的中点…

[读书笔记] 《修炼之道:.NET 开发要点精讲》

《修炼之道:.NET 开发要点精讲》目录《修炼之道:.NET 开发要点精讲》第 1 章 另辟蹊径&#xff1a;解读.NET1.7 本章思考 > 位置 465第 2 章 高屋建瓴&#xff1a;梳理编程约定2.2 方法与线程的关系 > 位置 5192.7 线程安全 > 位置 5952.8 调用与回调 > 位置 6612.…

ASP.NET Core 使用 gRPC 初探

&#xff08;RPC通讯示意图&#xff09;为什么突然说到gRPC呢&#xff0c;其实以前就想说一说这个东西&#xff0c;也想尝试使用一下&#xff0c;一直没有机会&#xff0c;一直看我公众号的小伙伴肯定都知道&#xff0c;这几天一直在录制一个《eShopOnContainer微服务架构》系列…

源码都没调试过,怎么能说熟悉 redis 呢?

一&#xff1a;背景 1. 讲故事记得在很久之前给初学的朋友们录制 redis 视频课程&#xff0c;当时结合了不少源码进行解读&#xff0c;自以为讲的还算可以&#xff0c;但还是有一个非常核心的点没被分享到&#xff0c;那就是源码级调试&#xff0c; 对&#xff0c;读源码还远远…

算法 - DFS/BFS

写DFS函数的时候首先确定当前位置是否已经加入路径 DFS函数大概率会传递“位置信息”&#xff0c;根据位置信息获取下一步的选择&#xff0c;&#xff08;大部分是在循环中&#xff09;选择、执行、回退 在哪做选择&#xff0c;就在哪退出选择&#xff0c;参考题9 def DFS()…

你想象中的Task后续,很简单?

【导读】前不久&#xff0c;写过一篇关于Task的简短文章&#xff0c;通过评论和转载得到好评&#xff0c;刚好我昨晚又写了一篇实现简单的消息队列也提到了Task&#xff0c;难道不应该是看具体执行什么操作&#xff0c;再考虑最佳方案&#xff1f;本文我们再次通过简短内容谈谈…

5G在工业互联网应用的机遇与挑战

移动通讯经过十年一代的发展&#xff0c;已经从1G发展到了5G&#xff0c;峰值速率实现十年千倍的增长&#xff0c;1G到4G是面向个人的&#xff0c;而5G是面向产业互联网和智慧城市服务。5G是一个颠覆性的技术&#xff0c;低时延&#xff08;每秒钟下载一部高清电影&#xff09;…

[C#.NET 拾遗补漏]10:理解 volatile 关键字

要理解 C# 中的 volatile 关键字&#xff0c;就要先知道编译器背后的一个基本优化原理。比如对于下面这段代码&#xff1a;public class Example {public int x;public void DoWork(){x 5;var y x 10;Debug.WriteLine("x " x ", y " y);} }在 Releas…

跟我一起学.NetCore之MediatR好像有点火

前言随着微服务的流行&#xff0c;而DDD(领域驱动设计)也光速般兴起&#xff0c;CRQS(Command Query Responsibility Seperation--命令查询职责分离)、领域事件名词是不是经常在耳边环绕&#xff0c;而MediatR组件经常用来对其技术的落地&#xff0c;凭这&#xff0c;小伙伴们说…

不想写脚本清理 mongodb 中的垃圾数据,ttlIndex 能帮到你!

mongodb一直都在不断的更新&#xff0c;不断的发展&#xff0c;那些非常好玩也非常实用的功能都逐步加入到了mongodb中&#xff0c;这不就有了本篇对ttlindex的介绍&#xff0c;刚好我们的生产业务场景中就有一个案例。。。一&#xff1a;案例分析 生产的推荐系统要给用户发送短…

后端学习 - 基础 《Java编程的逻辑》读书笔记

文章目录一 基础概念1 有关Java2 JVM / JDK / JRE3 与C的联系和区别4 各类型数据占用空间大小5 和 equals() 的区别、hashCode() 方法6 包装类型7 final 关键字8 参数传递机制&#xff1a;值传递9 String 的内存情况10 访问修饰符11 引用拷贝、浅拷贝与深拷贝三 面向对象1 面向…

cheatengine找不到数值_彩票中奖500万,领了还不到一半?这些问题不解决,钱都拿不走...

长期以来&#xff0c;“一夜暴富”是很多人梦寐以求的梦想&#xff0c;而作为最能让人“一夜暴富”的方式要数我国的福利彩票了&#xff0c;这也是很多人最容易活动暴富的机会&#xff0c;不少彩民长久以来一直买彩票的梦想就是“一夜暴富”。而突然暴富是很多人的梦想&#xf…

一站式Web开发套件BeetleX.WebFamily

BeetleX.WebFamily是一款前后端分离的Web开发套件&#xff0c;但它并不依赖于nodejs/npm/webpack等相关工具&#xff1b;而使用自身实现的方式来完成前后端分离的Web应用开发&#xff1b;套件以组件的方式发布&#xff0c;只需要在项目引用相关组件即可实现前后端分离开发&…

.NET架构小技巧(2)——访问修饰符正确姿势

在C#中&#xff0c;访问修饰符是使用频率很高的一组关键字&#xff0c;一共四个单词六个组合&#xff1a;public,internal,protected internal,protected,private protected,private&#xff0c;如果你对这些关键字非常清楚&#xff0c;请跳过&#xff0c;节省时间&#xff1b;…

能源36号文解读_IDC报告预测:今年中国新能源汽车销量将达116万辆,未来五年复合增长率36%_详细解读_最新资讯_热点事件...

编者按&#xff1a;本文来自36氪「 未来汽车日报」&#xff0c;(微信公众号ID&#xff1a;auto-time)&#xff0c;作者&#xff1a;秦章勇。 来源&#xff1a;IDC作者 | 秦章勇编辑 | 周游12月3日&#xff0c;在2020世界智能汽车大会上&#xff0c;IDG亚洲(国际数据(亚洲)集团)…

后端学习 - 容器

文章目录一 简介二 底层数据结构总结1 List2 Set3 Queue4 Map三 Collection 的子接口 List1 ArrayList 与 Vector2 ArrayList 与 LinkedList3 ArrayList 的 JDK 7/8 差异4 ArrayList 的构造方法与扩容机制*四 Collection 的子接口 Set1 HashSet、LinkedHashSet 和 TreeSet2 Has…

简单聊聊AspNetCore的启动流程

首先&#xff0c;得和大家达成一个共识&#xff0c;即AspNetCore项目其实就是一个控制台项目。可以简单的理解&#xff0c;AspNetCore就是将一个Web服务器宿主在一个进程(即控制台)中&#xff0c;然后它在这个进程中进行http请求的监听处理。AspNetCore中默认使用kestrel作为we…

共聚焦图片怎么加标尺_聚焦扶贫政策,打造小康生活

导语&#xff1a;农村独栋小楼、整洁的水泥路……扶贫产业蓬勃发展&#xff0c;我省结合实际&#xff0c;狠抓特色产业&#xff0c;助力脱贫攻坚&#xff0c;实现乡村振兴。武宁县&#xff1a;“四个聚焦”巩固脱贫成果2020年是全面建成小康社会目标实现之年&#xff0c;是全面…

后端学习 - 并发编程

文章目录零 基本概念1 CAS、ABA 问题和原子变量2 this 引用逸出3 不变性 immutable4 同步、异步、阻塞、非阻塞5 JMM6 同步方案演示&#xff1a;计数器 demo*一 进程与线程1 区别与联系2 Java内存区域3 线程组4 线程的上下文切换5 并发与并行6 线程的生命周期与状态二 线程间的…

打造跨平台.NET Core后台服务

续之前讲的在TopShelf上部署ASP.NET Core程序&#xff0c;作为后台服务运行&#xff0c;自从.NET Core 3.0出现以后&#xff0c;出现了自带的Generic Host&#xff0c;使得自托管服务变为可能。这种方式和TopShelf方式一样&#xff0c;可以直接F5进行服务的调试&#xff0c;也为…