• 再谈基于Ocelot API网关和IdentityServer的API认证与授权


    在《 再谈使用IdentityServer实现ASP.NET Core Web API的认证与授权》一文中,我又重新总结了IdentityServer中ApiScope和ApiResource的用法,通过对两者的设置,实现了基于Claim的API授权。今天,我们更进一步,引入Ocelot API网关,并由Ocelot API网关来传递Claims实现授权。

    理论上讲,在引入Ocelot API网关后,API的访问授权可以有以下三种模式:

    1. 完全由Ocelot托管,只需要在配置中设置RouteClaimsRequirement即可
    2. 由API自己处理,使用Identity中的Claims信息来设置授权策略
    3. 由API自己处理,Ocelot仅将Claims信息传递给API,API使用自定义逻辑完成授权

    这里我们讨论第二种模式。

    首先引入Ocelot API网关,可以参考我前面写的《 ASP.NET Core中Ocelot的使用:API网关的应用 》一文。之后,配置Ocelot API网关,使其使用IdentityServer的身份认证:

    builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer("management-provider-key", options =>
    {
        options.Authority = idsAuthority;
        options.Audience = "management";
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当然,这里有必要列出所使用的Ocelot的配置文件:

    {
      "Routes": [
        {
          "DownstreamPathTemplate": "/api/{everything}",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 9002
            }
          ],
          "UpstreamPathTemplate": "/meeting-room-service/{everything}",
          "UpstreamHttpMethod": [
            "Get",
            "Post",
            "Put",
            "Patch",
            "Delete",
            "Options"
          ],
          "SwaggerKey": "MeetingRoomService",
          "AuthenticationOptions": {
            "AuthenticationProviderKey": "management-provider-key",
            "AllowedScopes": [
              "management.read",
              "management.create"
            ]
          },
          "AddClaimsToRequest": {
            "scope": "Claims[scope] > value"
          }
        }
      ]
    }
    • 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

    此处定义了一个API端点的路由逻辑,并且在AuthenticationOptions配置中,设置了以下几个参数:

    1. AuthenticationProviderKey:与上文C#代码中AddJwtBearer方法调用的第一个参数匹配,表示这个API端点将使用之前所设置的那个Jwt Bearer认证方式相匹配,而在AddJwtBearer方法中,已经说明了Audience为management
    2. AllowedScopes表示该API所允许的ApiScope有哪些,可以参考前一篇文章来了解细节

    此外,我们还使用了AddClaimsToRequest这个配置 ,表示希望Ocelot能够将User Claims传递到下游API中。

    接下来,我们在上文中所使用的API中,将认证与授权相关的代码全部注释掉,直接同时运行Ocelot API网关、IdentityServer和API,然后调试GET请求,可以看到,虽然我们在Ocelot的配置中启用了AddClaimsToRequest,但在User Identity中并不能取得它的值:

    这里就需要在API中,对获取的Access Token进行解析,由于我们已经获得了Access Token,所以,从流程上看,并不需要API再去访问IdentityServer来验证Access Token,只需要解析就可以了。在API中添加下面的代码:

    builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(options =>
    {
        options.RequireHttpsMetadata = false;
        options.SaveToken = true;
        options.TokenValidationParameters = CreateTokenValidationParameters();
    });
    
    app.UseAuthentication();
    
    static TokenValidationParameters CreateTokenValidationParameters() => new()
    {
        ValidateIssuer = false,
        ValidateAudience = false,
        ValidateIssuerSigningKey = false,
        SignatureValidator = delegate (string token, TokenValidationParameters parameters)
        {
            var jwt = new JwtSecurityToken(token);
            return jwt;
        },
        RequireExpirationTime = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero,
        RequireSignedTokens = false
    };
    • 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

    此时再次启动调试,查看User Claims,可以看到,我们已经能够拿到正确的值:

    注意其中的scope Claim,也正是我们在进行基于ApiScope授权时所需要的。

    重新启用API中被注释掉的授权相关的代码,再次调试API,如果在获取Access Token时,包含了management.read的Scope:

    则可以访问GET方法:

    但如果在请求Access Token的时候,没有包含management.read Scope:

    则访问API就会得到403 Forbidden的错误:

  • 相关阅读:
    软件设计模式白话文系列(十四)策略模式
    Maven是什么?手把手先创建个Maven项目
    Kafka实现保证一批消息顺序生产消费的方案
    MySQL——简单认识一下索引吧(index)
    Linux C语言开发-D1Linux命令
    2个月的时间备考PMP时间够用吗?
    面试官:说说Netty对象池的实现原理?
    centralwidget 不能布局
    面试官:count(1)、count(*) 与 count(列名) 有什么区别?
    分库分表-分片算法运用
  • 原文地址:https://blog.csdn.net/m0_57042151/article/details/126067985