首页
归档
关于
Search
1
C服务器端
9 阅读
2
1.数据流图(下午题)
8 阅读
3
管道处理模型
8 阅读
4
数据结构与算法
8 阅读
5
3.面向对象设计
7 阅读
软件设计师笔记
.Net
Java
数据库
PHP
运维
前端
Python
中间件相关
云原生
架构设计
Search
标签搜索
websocket
科技新闻
Bi8bo
累计撰写
267
篇文章
累计收到
2
条评论
首页
栏目
软件设计师笔记
.Net
Java
数据库
PHP
运维
前端
Python
中间件相关
云原生
架构设计
页面
归档
关于
搜索到
17
篇与
的结果
2025-04-03
1.OAuth 的简单理解
OAuth 2.0 的一个简单解释作者: 阮一峰日期: 2019年4月 4日OAuth 2.0 是目前最流行的授权机制,用来授权第三方应用,获取用户数据。这个标准比较抽象,使用了很多术语,初学者不容易理解。其实说起来并不复杂,下面我就通过一个简单的类比,帮助大家轻松理解,OAuth 2.0 到底是什么。一、快递员问题我住在一个大型的居民小区。小区有门禁系统。进入的时候需要输入密码。我经常网购和外卖,每天都有快递员来送货。我必须找到一个办法,让快递员通过门禁系统,进入小区。如果我把自己的密码,告诉快递员,他就拥有了与我同样的权限,这样好像不太合适。万一我想取消他进入小区的权力,也很麻烦,我自己的密码也得跟着改了,还得通知其他的快递员。有没有一种办法,让快递员能够自由进入小区,又不必知道小区居民的密码,而且他的唯一权限就是送货,其他需要密码的场合,他都没有权限?二、授权机制的设计于是,我设计了一套授权机制。第一步,门禁系统的密码输入器下面,增加一个按钮,叫做"获取授权"。快递员需要首先按这个按钮,去申请授权。第二步,他按下按钮以后,屋主(也就是我)的手机就会跳出对话框:有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。我确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。第三步,门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。令牌就是类似密码的一串数字,只在短期内(比如七天)有效。第四步,快递员向门禁系统输入令牌,进入小区。有人可能会问,为什么不是远程为快递员开门,而要为他单独生成一个令牌?这是因为快递员可能每天都会来送货,第二天他还可以复用这个令牌。另外,有的小区有多重门禁,快递员可以使用同一个令牌通过它们。三、互联网场景我们把上面的例子搬到互联网,就是 OAuth 的设计了。首先,居民小区就是储存用户数据的网络服务。比如,微信储存了我的好友信息,获取这些信息,就必须经过微信的"门禁系统"。其次,快递员(或者说快递公司)就是第三方应用,想要穿过门禁系统,进入小区。最后,我就是用户本人,同意授权第三方应用进入小区,获取我的数据。简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。四、令牌与密码令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。(2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。(3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细。具体来说,一共分成四种授权类型(authorization grant),即四种颁发令牌的方式,适用于不同的互联网场景。下一篇文章,我就来介绍这四种类型,并给出代码实例。(完)
2025年04月03日
5 阅读
0 评论
0 点赞
2025-04-03
2.OAuth 详细说明
暂无简介
2025年04月03日
5 阅读
0 评论
0 点赞
2025-04-03
再谈使用IdentityServer实现ASP.NET Core Web API的认证与授权 - dax.net - 博客园
[ ](https://www.cnblogs.com/)会员周边众包 [新闻](https://news.cnblogs.com/) [博问](https://q.cnblogs.com/) [闪存](https://ing.cnblogs.com/)赞助商 所有博客 当前博客 [ ](https://i.cnblogs.com/EditPosts.aspx?opt=1)[ ](https://passport.cnblogs.com/GetBlogApplyStatus.aspx)[ ](https://msg.cnblogs.com/)[ ](javascript:void(0)) [ ](https://home.cnblogs.com/) [我的博客](https://passport.cnblogs.com/GetBlogApplyStatus.aspx) [我的园子](https://home.cnblogs.com/) [账号设置](https://account.cnblogs.com/settings/account) [ 简洁模式 ...](javascript:void(0)) [退出登录](javascript:void(0)) [注册](https://account.cnblogs.com/signup)[登录](javascript:void(0);) [](https://www.cnblogs.com/daxnet/)dax.netSoftware on .NET/C#, Cloud, Microservices and DDD,专业、求是、解惑......博客园首页订阅管理 随笔 - 309 文章 - 0 评论 - 2754 阅读 - 183万 [ 再谈使用IdentityServer实现ASP.NET Core Web API的认证与授权 ](https://www.cnblogs.com/daxnet/p/18114009) 在《[使用Ocelot、IdentityServer4、Spring Cloud Eureka搭建微服务网关:Step by Step(一)](https://sunnycoding.cn/2019/02/14/microservices-with-ocelot-id4-and-eureka-part1)》一文中,我曾经介绍了如何使用IdentityServer4对ASP.NET Core Web API的访问进行身份认证与授权。本文将更加深入讨论有关ApiResource与ApiScope相关的内容。注意:从2022年12月3日开始,社区版的IdentityServer4将不再被支持,所有的后续开发工作都将在Duende Software组织下进行。这也就意味着,IdentityServer将采用商用许可,不过,对于个人的开发、测试需求,以及年利润小于100万美元的个人或公司项目,IdentityServer仍然免费。因此,在本文的介绍中,将不再使用“IdentityServer4”来介绍,而使用“IdentityServer”这个名字。在讨论IdentityServer中的相关概念时,可以思考一下,一个基于角色的权限管理系统(RBAC)是如何实现的。总的来说,RBAC关心的是:用户或者用户组,对于什么样的资源,具有什么样的访问权限。比如:用户组A对于天气服务具有读取的权限,而对于国家信息查询服务则没有访问权限,而用户组B则恰好相反。在IdentityServer中,可以类比地将用户组看成Client;将天气服务、国家信息查询服务看成ApiResource,于是,访问权限的标记,则被看成是ApiScope。为什么说是“访问权限的标记”而不是“访问权限”呢?因为在IdentityServer中,访问权限的定义,是通过Client的AllowedScope以及API的Audience来设定的。ApiScope定义了一组标记,ApiResource可以使用这些标记来决定它能够支持哪些类型的权限设定,而Client又可以指定它能够使用哪些ApiScope来访问API。下面的代码中定义了3个ApiResource,通常情况下,一个ApiResource可以对应一个需要被IdentityServer鉴权的ASP.NET Core Web API应用程序:?| 12345678910111213141516 | publicstaticIEnumerable ApiResources => newApiResource[] { newApiResource("management", "Meeting Room Management API") { Scopes = { "management.read", "management.create", "management.update", "management.delete"} }, newApiResource("reservation", "Meeting Room Reservation API") { Scopes = { "reservation.read", "reservation.create", "reservation.update", "reservation.delete"} }, newApiResource("audit", "Audit API") { Scopes = { "audit.query", "audit.insert"} } }; | 在每个ApiResource定义中,它包含了三个信息:ApiResource的名称、描述,以及它所包含的ApiScope。例如:对于audit ApiResource,它的名称是audit,描述是Audit API,然后它所支持的ApiScope包括audit.query和audit.insert。ApiScope的定义如下:?| 12345678910111213141516171819 | publicstaticIEnumerable ApiScopes => newApiScope[] { // Management API scopes newApiScope("management.read", "Retrieves the meeting room information."), newApiScope("management.create", "Creates the meeting room."), newApiScope("management.update", "Updates the meeting room."), newApiScope("management.delete", "Deletes the meeting room."), // Reservation API scopes newApiScope("reservation.read", "Retrieves the meeting room reservation information."), newApiScope("reservation.create", "Reserves a meeting room."), newApiScope("reservation.update", "Updates the reservation."), newApiScope("reservation.delete", "Cancels the reservation."), // Audit API scopes newApiScope("audit.query", "Queries the audit information."), newApiScope("audit.insert", "Inserts a audit record.") }; | 可以看到,ApiScope其实就是定义了一个字符串标记,这个标记建立了Client与ApiResource之间的关系,不仅如此,ApiScope还会以Claim的形式传递给受保护的Web API,以便完成API访问授权。那么对于一个IdentityServer Client,它就可以指定所允许使用的ApiScope有哪些:?| 1234567891011121314 | publicstaticIEnumerable Clients => newClient[] { // m2m client credentials flow client newClient { ClientId = "m2m.client", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { newSecret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, AllowedScopes = { "management.read", "management.create"} }, }; | 在上面的代码中,m2m.client这个Client它指定了AllowedScope只能是management.read和management.create,因此,对于这个Client而言,它只能用来访问management这个ApiResource(其实也就是Audience为management的ASP.NET Core Web API应用程序),并且仅能访问这个ApiResource中被标记为management.read和management.create的API端点。这一部分后续会介绍。不要忘记在ASP.NET Core中使用IdentityServer:?| 1234567891011121314 | builder.Services .AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; options.EmitStaticAudienceClaim = true; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiResources(Config.ApiResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddAspNetIdentity(); | 配置好IdentityServer应用程序后,启动应用程序,然后使用Postman来调用/connect/token这个API端点,以获得Jwt Access Token:[](https://sunnycoding.cn/wp-content/uploads/2022/07/20220710_postman_connect_token.png)对应的cURL命令如下:?| 1234567 | curl -L -X POST 'https://localhost:9001/connect/token'\<br />-H 'Content-Type: application/x-www-form-urlencoded'\<br />--data-urlencode 'client_id=m2m.client'\<br />--data-urlencode 'client_secret=511536EF-F270-4058-80CA-1C89C192F69A'\<br />--data-urlencode 'grant_type=client_credentials'\<br />--data-urlencode 'response_type=id_token'\<br />--data-urlencode 'scope=management.read management.create' | 此时,会请求/connect/token端点,通过传入client_id、client_secret、grant_type、response_type以及scope这些参数,来获得Jwt Access Token。这里的scope必须是之前Client中定义的AllowedScopes的子集,或者与之相同,否则会出现invalid_scope的错误。其它的参数也都是与Client的设置相匹配,因此,通过调用这个API获得的Jwt Access Token就只能访问management API,并且只能访问被标记为“读取”(read)和“创建”(create)的API端点。不过,这还需要配合受保护的Web API应用程序中对于授权(Authorization)的实现。接下来,配置我们的Web API应用程序,使其受保护于IdentityServer。在Program.cs中,加入下面的代码:?| 12345678 | builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://localhost:9001"; options.Audience = "management"; options.TokenValidationParameters.ValidTypes = new[] { "at+jwt"}; });app.UseAuthentication(); | 注意这里的Authority为IdentityServer的地址,Audience则为IdentityServer中所设置的对应的ApiResource的名字。上面所获得的Access Token所包含的scope信息为management.read和management.create,两者都是management这个ApiResource所定义的ApiScope,因此,这个Access Token是可以访问我们的Web API的,因为它的Audience正是management。接下来,在Controller上加上Authorize特性即可:?| 1234567 | [ApiController][Route("api/[controller]")][Authorize]publicclassMeetingRoomsController : ControllerBase{ // ...} | 此时,如果使用Postman来访问MeetingRoomsController中的Get方法,则会返回401 Unauthorized的错误:[](https://sunnycoding.cn/wp-content/uploads/2022/07/20220712_postman_401.png)现在,在Postman中,将Auth的Type切换为Bearer Token,然后将Token的值设置为上面所获得的Access Token,再次调用上面的API,此时返回200 OK,调用成功:[](https://sunnycoding.cn/wp-content/uploads/2022/07/20220712_postman_200.png)ASP.NET Core Web API应用程序还可以基于IdentityServer中所设定的ApiScope来进行基于Claim的访问授权。比如:这里的Access Token仅具有management.read和management.create这两个scope,因此,它理应只能访问Web API应用程序中具有读取和创建功能的API,这可以通过配置Web API应用程序来实现。在Web API应用程序的Program.cs中,加入下面的代码:?| 1234567 | builder.Services.AddAuthorization(options =>{ options.AddPolicy("management.read", policy => policy.RequireClaim("scope", "management.read")); options.AddPolicy("management.create", policy => policy.RequireClaim("scope", "management.create")); options.AddPolicy("management.update", policy => policy.RequireClaim("scope", "management.update")); options.AddPolicy("management.delete", policy => policy.RequireClaim("scope", "management.delete"));}); | 这段代码在Web API授权体系中加入了4个策略(Policy),这些Policy会要求Claims中有一个名为scope的Claim,并且具有所对应的值。于是,被标记为management.read这一授权策略的API,在被访问前,ASP.NET Core Web API就会检查Claims中是否有一个的scope为management.read,如果没有,则返回403 Forbidden。要使用Authorization Policy对API进行标记,需要在Controller中的不同的Action上使用Authorize特性来标记,它告诉Web API框架,当前这个API需要采用什么样的授权策略。下面的代码展示了如何使用Authorize特性来针对不同的API采用不同的授权策略:?| 123456789101112131415161718192021222324252627282930313233343536373839 | [ApiController][Route("api/[controller]")]publicclassMeetingRoomsController : ControllerBase{ [HttpPost] [Authorize("management.create")] publicasync Task CreateMeetingRoom([FromBody] MeetingRoom meetingRoom) { // ... } [HttpDelete("{id}")] [Authorize("management.delete")] publicasync Task DeleteMeetingRoom(longid) { // ... } [HttpGet("{id}")] [Authorize("management.read")] publicIActionResult GetMeetingRoom(longid) { // ... } [HttpGet] [Authorize("management.read")] publicIActionResult GetMeetingRooms([FromQuery] intpageSize = 10, intpageNumber = 0) { // ... } [HttpPatch("{id}")] [Authorize("management.update")] publicasync Task PatchMeetingRoom(longid, [FromBody] JsonPatchDocument patchDoc) { // ... }} | 现在,如果使用同样的Access Token来访问GetMeetingRoom和GetMeetingRooms这两个API,则都能成功。然而,如果访问DeleteMeetingRoom这个API,则返回403 Forbidden。因为这个Access Token没有包含management.delete这个scope:[](https://sunnycoding.cn/wp-content/uploads/2022/07/20220712_postman_403.png)到此为止,我们已经在ASP.NET Core Web API下实现了基于IdentityServer的ApiResource和ApiScope的认证与授权。 [好文要顶](javascript:void(0);) [关注我](javascript:void(0);) [收藏该文](javascript:void(0);) [微信分享](javascript:void(0);) [](https://home.cnblogs.com/u/daxnet/) [dax.net](https://home.cnblogs.com/u/daxnet/) [粉丝 - 3067](https://home.cnblogs.com/u/daxnet/followers/) [关注 - 21](https://home.cnblogs.com/u/daxnet/followees/) [会员号:3853](https://cnblogs.vip/) 推荐博客 [+加关注](javascript:void(0);) 0 0 升级成为会员 « 上一篇: 使用C#和MonoGame开发俄罗斯方块游戏[» ](https://www.cnblogs.com/daxnet/p/18114010) 下一篇: [再谈基于Ocelot API网关和IdentityServer的API认证与授权](https://www.cnblogs.com/daxnet/p/18114010) posted @ 2022-07-12 21:41 dax.net 阅读(24) 评论(0) 编辑 收藏) 举报) 会员力量,点亮园子希望刷新页面返回顶部登录后才能查看或发表评论,立即 [登录](javascript:void(0);) 或者 [逛逛](https://www.cnblogs.com/) 博客园首页 [【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步](http://ishell.cc/) [ ](https://cnblogs.vip/) **编辑推荐:**· 架构师备考的一些思考 · 利用分布式锁在 ASP.NET Core 中实现防抖 · 写在临近40岁的年龄 · 一场 Kafka CRC 异常引发的血案 · 记录荒废了三年的四年.net开发的第三次面试 **阅读排行:** · 40岁大龄失业程序猿,未来该何去何从 · .NET 8.0 文档管理系统网盘功能的实现 · 神奇的C语言输出12天圣诞节歌词代码 · 牛逼!Vue3.5的useTemplateRef让ref操作DOM更加丝滑 · 一个开源、跨平台的.NET UI框架 - Avalonia UI**历史上的今天:**2017-07-12 在Apworks数据服务中使用基于Entity Framework Core的仓储(Repository)实现公告 陈晴阳(Sunny Chen),2003年毕业于中南大学铁道校区(原长沙铁道学院),高级程序员(2004年),系统分析员(2006年),微软认证专家(MCP 2004),前微软最有价值专家(MVP,2012-2019)。cnblogs网名daxnet,并打算以该名闯荡IT江湖。曾经使用Microsoft Dynamics AX近六年,现专注于Microsoft .NET、.NET Core和Visual C#,是领域驱动设计与微服务、云架构设计的忠实爱好者和实践者。 昵称: [ dax.net ](https://home.cnblogs.com/u/daxnet/) 园龄: [ 14年5个月 ](https://home.cnblogs.com/u/daxnet/) 荣誉: 推荐博客粉丝: [ 3067 ](https://home.cnblogs.com/u/daxnet/followers/) 关注: [ 21 ](https://home.cnblogs.com/u/daxnet/followees/) 2024年9月日一二三四五六123456789101112131415161718192021222324252627282930123456789101112积分与排名 积分 - 505693 排名 - 1320 [随笔分类](https://www.cnblogs.com/daxnet/post-categories) (372) .NET Core(27).NET/C#(105).NET应用框架架构(9)AI(1)Apworks(39)ASP.NET MVC(3)CloudNotes(7)Docker(7)Dynamics AX(19)Kubernetes(2)Microsoft XNA(5)NLayerApp(9)Tiny Library CQRS(6)技术应用(23)精彩生活(13)领域驱动设计(76)领域特定语言(DSL)(3)设计模式(4)树莓派(2)微服务(12) [更多](javascript:void(0)) 随笔档案(309) 2024年8月(1)2024年7月(1)2024年5月(1)2024年4月(3)2024年1月(1)2023年12月(1)2023年5月(1)2023年4月(2)2023年3月(1)2023年1月(1)2022年10月(1)2022年9月(1)2022年8月(1)2022年7月(3)2022年5月(2)2022年3月(1)2022年2月(1)2022年1月(1)2021年12月(1)2021年11月(2)2021年8月(2)2021年3月(1)2021年2月(2)2020年5月(1)2020年3月(3)2020年2月(3)2019年10月(2)2019年9月(1)2019年7月(3)2019年6月(1)2019年5月(3)2019年4月(1)2019年3月(2)2019年2月(2)2019年1月(2)2018年11月(4)2018年10月(5)2018年9月(2)2018年8月(2)2018年7月(3)2018年6月(1)2018年3月(5)2018年2月(1)2018年1月(1)2017年12月(1)2017年11月(2)2017年10月(1)2017年7月(1)2017年6月(1)2017年5月(1)2017年4月(2)2017年3月(1)2017年1月(1)2016年12月(2)2016年9月(1)2016年8月(3)2016年4月(2)2016年3月(2)2015年10月(4)2015年9月(1)2015年8月(2)2015年7月(1)2015年2月(4)2015年1月(1)2014年12月(1)2014年8月(1)2014年4月(3)2014年2月(1)2013年10月(2)2013年8月(3)2013年5月(6)2013年4月(4)2013年3月(1)2013年2月(4)2013年1月(2)2012年12月(2)2012年11月(3)2012年8月(1)2012年7月(2)2012年6月(2)2012年4月(1)2012年3月(3)2012年2月(1)2012年1月(3)2011年12月(1)2011年11月(3)2011年10月(1)2011年9月(4)2011年8月(2)2011年7月(4)2011年6月(5)2011年5月(2)2011年3月(4)2011年2月(6)2011年1月(8)2010年12月(4)2010年11月(3)2010年10月(4)2010年9月(3)2010年8月(4)2010年7月(13)2010年5月(1)2010年4月(2)2010年3月(3)2010年2月(6)2010年1月(11)2009年12月(1)2009年11月(4)2009年9月(3)2009年8月(3)2009年7月(3)2009年5月(8)2009年4月(1)2009年3月(6)2009年2月(7)2009年1月(1)2008年12月(6)2008年11月(12) [更多](javascript:void(0)) [推荐排行榜](https://www.cnblogs.com/daxnet/most-liked) [ 1. 领域驱动设计系列文章汇总(124) ](https://www.cnblogs.com/daxnet/archive/2010/11/02/1867392.html) [ 2. .NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系(66) ](https://www.cnblogs.com/daxnet/p/18299758) [ 3. ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现(61) ](https://www.cnblogs.com/daxnet/p/8082694.html) [ 4. 重温.NET下Assembly的加载过程(60) ](https://www.cnblogs.com/daxnet/p/8525249.html) [ 5. WeText项目:一个基于.NET实现的DDD、CQRS与微服务架构的演示案例(56) ](https://www.cnblogs.com/daxnet/p/5421871.html) 最新评论Re:基于MonoGame重制《俄罗斯方块》游戏@描我白 厉害了,博主。 谢谢支持,互相学习...--dax.netRe:基于MonoGame重制《俄罗斯方块》游戏厉害了,博主。--描我白Re:.NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系@dax.net 现在用.net开发人员我认为是无须了解.net standard这个玩意的。...--首席装逼官Re:.NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系@首席装逼官 net standard是一个netcore取代netframework的一个中间概念,主要是为了不同版本的做兼容性,现在可以不用考虑和关注这个概念了。 没错,只是现在还是有很多用.NE...--dax.netRe:.NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系还是那句:|微软误我啊.--迅捷网络[来送福利] Copyright © 2024 dax.netPowered by .NET 8.0 on Kubernetes 点击右上角即可分享 [ ](https://www.cnblogs.com/cmt/p/18302049)
2025年04月03日
6 阅读
0 评论
0 点赞
2025-04-03
EF 贪婪加载 , 延迟加载,显式加载
**延迟加载 ** 在遍历时才会执行查询**贪婪加载 ** 一次性加载所有数据 Include()方法,访问一次数据库 所有数据都有,数据过多**显式加载** 与延迟加载类似,但不会主动去加载导航属性的数据 而需要手动去加载关闭延迟加载 :1.特定的导航属性去掉visual 2.配置db.Configuration.LazyLoadingEnabled =false (关闭延迟加载)//显示加载//手动加载 var resultOrder = db.Orders.Find(3); // Reference 对应单个对象 db.Entry(order).Reference("user").Load(); //Collection 对应多个对象(集合) db.Entry(user).Collection("Order").Load();如果你要访问所有数据,可以使用贪婪加载。如果你不经常访问导航属性,或只访问部分,可以使用延迟加载
2025年04月03日
5 阅读
0 评论
0 点赞
2025-04-03
再谈使用IdentityServer实现ASP.NET Core Web API的认证与授权
在《使用Ocelot、IdentityServer4、Spring Cloud Eureka搭建微服务网关:Step by Step(一)》一文中,我曾经介绍了如何使用IdentityServer4对ASP.NET Core Web API的访问进行身份认证与授权。本文将更加深入讨论有关ApiResource与ApiScope相关的内容。注意:从2022年12月3日开始,社区版的IdentityServer4将不再被支持,所有的后续开发工作都将在Duende Software组织下进行。这也就意味着,IdentityServer将采用商用许可,不过,对于个人的开发、测试需求,以及年利润小于100万美元的个人或公司项目,IdentityServer仍然免费。因此,在本文的介绍中,将不再使用“IdentityServer4”来介绍,而使用“IdentityServer”这个名字。在讨论IdentityServer中的相关概念时,可以思考一下,一个基于角色的权限管理系统(RBAC)是如何实现的。总的来说,RBAC关心的是:用户或者用户组,对于什么样的资源,具有什么样的访问权限。比如:用户组A对于天气服务具有读取的权限,而对于国家信息查询服务则没有访问权限,而用户组B则恰好相反。 在IdentityServer中,可以类比地将用户组看成Client;将天气服务、国家信息查询服务看成ApiResource,于是,访问权限的标记,则被看成是ApiScope。为什么说是“访问权限的标记”而不是“访问权限”呢?因为在IdentityServer中,访问权限的定义,是通过Client的AllowedScope以及API的Audience来设定的。ApiScope定义了一组标记,ApiResource可以使用这些标记来决定它能够支持哪些类型的权限设定,而Client又可以指定它能够使用哪些ApiScope来访问API。 下面的代码中定义了3个ApiResource,通常情况下,一个ApiResource可以对应一个需要被IdentityServer鉴权的ASP.NET Core Web API应用程序:| 12345678910111213141516 | publicstaticIEnumerable ApiResources => newApiResource[] { newApiResource("management", "Meeting Room Management API") { Scopes = { "management.read", "management.create", "management.update", "management.delete"} }, newApiResource("reservation", "Meeting Room Reservation API") { Scopes = { "reservation.read", "reservation.create", "reservation.update", "reservation.delete"} }, newApiResource("audit", "Audit API") { Scopes = { "audit.query", "audit.insert"} } }; | 在每个ApiResource定义中,它包含了三个信息:ApiResource的名称、描述,以及它所包含的ApiScope。例如:对于audit ApiResource,它的名称是audit,描述是Audit API,然后它所支持的ApiScope包括audit.query和audit.insert。ApiScope的定义如下:| 12345678910111213141516171819 | publicstaticIEnumerable ApiScopes => newApiScope[] { // Management API scopes newApiScope("management.read", "Retrieves the meeting room information."), newApiScope("management.create", "Creates the meeting room."), newApiScope("management.update", "Updates the meeting room."), newApiScope("management.delete", "Deletes the meeting room."), // Reservation API scopes newApiScope("reservation.read", "Retrieves the meeting room reservation information."), newApiScope("reservation.create", "Reserves a meeting room."), newApiScope("reservation.update", "Updates the reservation."), newApiScope("reservation.delete", "Cancels the reservation."), // Audit API scopes newApiScope("audit.query", "Queries the audit information."), newApiScope("audit.insert", "Inserts a audit record.") }; | 可以看到,ApiScope其实就是定义了一个字符串标记,这个标记建立了Client与ApiResource之间的关系,不仅如此,ApiScope还会以Claim的形式传递给受保护的Web API,以便完成API访问授权。 那么对于一个IdentityServer Client,它就可以指定所允许使用的ApiScope有哪些:| 1234567891011121314 | publicstaticIEnumerable Clients => newClient[] { // m2m client credentials flow client newClient { ClientId = "m2m.client", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { newSecret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, AllowedScopes = { "management.read", "management.create"} }, }; | 在上面的代码中,m2m.client这个Client它指定了AllowedScope只能是management.read和management.create,因此,对于这个Client而言,它只能用来访问management这个ApiResource(其实也就是Audience为management的ASP.NET Core Web API应用程序),并且仅能访问这个ApiResource中被标记为management.read和management.create的API端点。这一部分后续会介绍。不要忘记在ASP.NET Core中使用IdentityServer:| 1234567891011121314 | builder.Services .AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; options.EmitStaticAudienceClaim = true; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiResources(Config.ApiResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddAspNetIdentity(); | 配置好IdentityServer应用程序后,启动应用程序,然后使用Postman来调用/connect/token这个API端点,以获得Jwt Access Token: [](https://sunnycoding.cn/wp-content/uploads/2022/07/20220710_postman_connect_token.png) 对应的cURL命令如下:| 1234567 | curl -L -X POST 'https://localhost:9001/connect/token'\<br />-H 'Content-Type: application/x-www-form-urlencoded'\<br />--data-urlencode 'client_id=m2m.client'\<br />--data-urlencode 'client_secret=511536EF-F270-4058-80CA-1C89C192F69A'\<br />--data-urlencode 'grant_type=client_credentials'\<br />--data-urlencode 'response_type=id_token'\<br />--data-urlencode 'scope=management.read management.create' | 此时,会请求/connect/token端点,通过传入client_id、client_secret、grant_type、response_type以及scope这些参数,来获得Jwt Access Token。这里的scope必须是之前Client中定义的AllowedScopes的子集,或者与之相同,否则会出现invalid_scope的错误。其它的参数也都是与Client的设置相匹配,因此,通过调用这个API获得的Jwt Access Token就只能访问management API,并且只能访问被标记为“读取”(read)和“创建”(create)的API端点。不过,这还需要配合受保护的Web API应用程序中对于授权(Authorization)的实现。 接下来,配置我们的Web API应用程序,使其受保护于IdentityServer。在Program.cs中,加入下面的代码:| 12345678 | builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://localhost:9001"; options.Audience = "management"; options.TokenValidationParameters.ValidTypes = new[] { "at+jwt"}; });app.UseAuthentication(); | 注意这里的Authority为IdentityServer的地址,Audience则为IdentityServer中所设置的对应的ApiResource的名字。上面所获得的Access Token所包含的scope信息为management.read和management.create,两者都是management这个ApiResource所定义的ApiScope,因此,这个Access Token是可以访问我们的Web API的,因为它的Audience正是management。 接下来,在Controller上加上Authorize特性即可:| 1234567 | [ApiController][Route("api/[controller]")][Authorize]publicclassMeetingRoomsController : ControllerBase{ // ...} | 此时,如果使用Postman来访问MeetingRoomsController中的Get方法,则会返回401 Unauthorized的错误: [](https://sunnycoding.cn/wp-content/uploads/2022/07/20220712_postman_401.png) 现在,在Postman中,将Auth的Type切换为Bearer Token,然后将Token的值设置为上面所获得的Access Token,再次调用上面的API,此时返回200 OK,调用成功: [](https://sunnycoding.cn/wp-content/uploads/2022/07/20220712_postman_200.png) ASP.NET Core Web API应用程序还可以基于IdentityServer中所设定的ApiScope来进行基于Claim的访问授权。比如:这里的Access Token仅具有management.read和management.create这两个scope,因此,它理应只能访问Web API应用程序中具有读取和创建功能的API,这可以通过配置Web API应用程序来实现。 在Web API应用程序的Program.cs中,加入下面的代码:| 1234567 | builder.Services.AddAuthorization(options =>{ options.AddPolicy("management.read", policy => policy.RequireClaim("scope", "management.read")); options.AddPolicy("management.create", policy => policy.RequireClaim("scope", "management.create")); options.AddPolicy("management.update", policy => policy.RequireClaim("scope", "management.update")); options.AddPolicy("management.delete", policy => policy.RequireClaim("scope", "management.delete"));}); | 这段代码在Web API授权体系中加入了4个策略(Policy),这些Policy会要求Claims中有一个名为scope的Claim,并且具有所对应的值。于是,被标记为management.read这一授权策略的API,在被访问前,ASP.NET Core Web API就会检查Claims中是否有一个的scope为management.read,如果没有,则返回403 Forbidden。要使用Authorization Policy对API进行标记,需要在Controller中的不同的Action上使用Authorize特性来标记,它告诉Web API框架,当前这个API需要采用什么样的授权策略。 下面的代码展示了如何使用Authorize特性来针对不同的API采用不同的授权策略:| 123456789101112131415161718192021222324252627282930313233343536373839 | [ApiController][Route("api/[controller]")]publicclassMeetingRoomsController : ControllerBase{ [HttpPost] [Authorize("management.create")] publicasync Task CreateMeetingRoom([FromBody] MeetingRoom meetingRoom) { // ... } [HttpDelete("{id}")] [Authorize("management.delete")] publicasync Task DeleteMeetingRoom(longid) { // ... } [HttpGet("{id}")] [Authorize("management.read")] publicIActionResult GetMeetingRoom(longid) { // ... } [HttpGet] [Authorize("management.read")] publicIActionResult GetMeetingRooms([FromQuery] intpageSize = 10, intpageNumber = 0) { // ... } [HttpPatch("{id}")] [Authorize("management.update")] publicasync Task PatchMeetingRoom(longid, [FromBody] JsonPatchDocument patchDoc) { // ... }} | 现在,如果使用同样的Access Token来访问GetMeetingRoom和GetMeetingRooms这两个API,则都能成功。然而,如果访问DeleteMeetingRoom这个API,则返回403 Forbidden。因为这个Access Token没有包含management.delete这个scope: [](https://sunnycoding.cn/wp-content/uploads/2022/07/20220712_postman_403.png) 到此为止,我们已经在ASP.NET Core Web API下实现了基于IdentityServer的ApiResource和ApiScope的认证与授权。
2025年04月03日
5 阅读
0 评论
0 点赞
1
2
...
4