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

By | 2022年7月14日

在《再谈使用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";
});

当然,这里有必要列出所使用的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"
      }
    }
  ]
}

此处定义了一个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
};

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

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

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

则可以访问GET方法:

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

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

(总访问量:279;当日访问量:2)

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据