再谈使用IdentityServer实现ASP.NET Core Web API的认证与授权 - dax.net - 博客园
标签搜索

再谈使用IdentityServer实现ASP.NET Core Web API的认证与授权 - dax.net - 博客园

admin
2025-04-03 / 0 评论 / 6 阅读 / 正在检测是否收录...
  •               [
                      
                  ](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.net

Software 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应用程序:
?

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | public
static
IEnumerable ApiResources =>
    
new
ApiResource[]
    
{
        
new
ApiResource(
"management"
,
"Meeting Room Management API"
)
        
{
            
Scopes = {
"management.read"
,
"management.create"
,
"management.update"
,
"management.delete"
}
        
},
        
new
ApiResource(
"reservation"
,
"Meeting Room Reservation API"
)
        
{
            
Scopes = {
"reservation.read"
,
"reservation.create"
,
"reservation.update"
,
"reservation.delete"
}
        
},
        
new
ApiResource(
"audit"
,
"Audit API"
)
        
{
            
Scopes = {
"audit.query"
,
"audit.insert"
}
        
}
    
}; |

在每个ApiResource定义中,它包含了三个信息:ApiResource的名称、描述,以及它所包含的ApiScope。例如:对于audit ApiResource,它的名称是audit,描述是Audit API,然后它所支持的ApiScope包括audit.query和audit.insert。ApiScope的定义如下:
?

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | public
static
IEnumerable ApiScopes =>
    
new
ApiScope[]
    
{
        
// Management API scopes
        
new
ApiScope(
"management.read"
,
"Retrieves the meeting room information."
),
        
new
ApiScope(
"management.create"
,
"Creates the meeting room."
),
        
new
ApiScope(
"management.update"
,
"Updates the meeting room."
),
        
new
ApiScope(
"management.delete"
,
"Deletes the meeting room."
),
 
        
// Reservation API scopes
        
new
ApiScope(
"reservation.read"
,
"Retrieves the meeting room reservation information."
),
        
new
ApiScope(
"reservation.create"
,
"Reserves a meeting room."
),
        
new
ApiScope(
"reservation.update"
,
"Updates the reservation."
),
        
new
ApiScope(
"reservation.delete"
,
"Cancels the reservation."
),
 
        
// Audit API scopes
        
new
ApiScope(
"audit.query"
,
"Queries the audit information."
),
        
new
ApiScope(
"audit.insert"
,
"Inserts a audit record."
)
    
}; |

可以看到,ApiScope其实就是定义了一个字符串标记,这个标记建立了Client与ApiResource之间的关系,不仅如此,ApiScope还会以Claim的形式传递给受保护的Web API,以便完成API访问授权。那么对于一个IdentityServer Client,它就可以指定所允许使用的ApiScope有哪些:
?

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | public
static
IEnumerable Clients =>
    
new
Client[]
    
{
        
// m2m client credentials flow client
        
new
Client
        
{
            
ClientId =
"m2m.client"
,
            
ClientName =
"Client Credentials Client"
,
 
            
AllowedGrantTypes = GrantTypes.ClientCredentials,
            
ClientSecrets = {
new
Secret(
"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:
?

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | 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命令如下:
?

| 1
2
3
4
5
6
7 | 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中,加入下面的代码:
?

| 1
2
3
4
5
6
7
8 | 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特性即可:
?

| 1
2
3
4
5
6
7 | [ApiController]
[Route(
"api/[controller]"
)]
[Authorize]
public
class
MeetingRoomsController : 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中,加入下面的代码:
?

| 1
2
3
4
5
6
7 | 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采用不同的授权策略:
?

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 | [ApiController]
[Route(
"api/[controller]"
)]
public
class
MeetingRoomsController : ControllerBase
{
    
[HttpPost]
    
[Authorize(
"management.create"
)]
    
public
async Task CreateMeetingRoom([FromBody] MeetingRoom meetingRoom)
    
{
        
// ...
    
}
 
    
[HttpDelete(
"{id}"
)]
    
[Authorize(
"management.delete"
)]
    
public
async Task DeleteMeetingRoom(
long
id)
    
{
        
// ...
    
}
 
    
[HttpGet(
"{id}"
)]
    
[Authorize(
"management.read"
)]
    
public
IActionResult GetMeetingRoom(
long
id)
    
{
        
// ...
    
}
 
    
[HttpGet]
    
[Authorize(
"management.read"
)]
    
public
IActionResult GetMeetingRooms([FromQuery]
int
pageSize = 10,
int
pageNumber = 0)
    
{
        
// ...
    
}
 
    
[HttpPatch(
"{id}"
)]
    
[Authorize(
"management.update"
)]
    
public
async Task PatchMeetingRoom(
long
id, [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月
1234567
891011121314
15161718192021
22232425262728
293012345
6789101112

积分与排名

  •   积分 -    

    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)            

    最新评论

    1. Re:基于MonoGame重制《俄罗斯方块》游戏
  • @描我白 厉害了,博主。 谢谢支持,互相学习...
  • --dax.net
    1. Re:基于MonoGame重制《俄罗斯方块》游戏
  • 厉害了,博主。
  • --描我白
    1. Re:.NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系
  • @dax.net 现在用.net开发人员我认为是无须了解.net standard这个玩意的。...
  • --首席装逼官
    1. Re:.NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系
  • @首席装逼官 net standard是一个netcore取代netframework的一个中间概念,主要是为了不同版本的做兼容性,现在可以不用考虑和关注这个概念了。 没错,只是现在还是有很多用.NE...
  • --dax.net
    1. Re:.NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系
  • 还是那句:
    |微软误我啊.
  • --迅捷网络[来送福利] Copyright © 2024 dax.net
    Powered by .NET 8.0 on Kubernetes
    点击右上角即可分享

    [

    
    ](https://www.cnblogs.com/cmt/p/18302049)
    
0

评论 (0)

取消