在《 再谈使用IdentityServer实现ASP.NET Core Web API的认证与授权》一文中,我又重新总结了IdentityServer中ApiScope和ApiResource的用法,通过对两者的设置,实现了基于Claim的API授权。今天,我们更进一步,引入Ocelot API网关,并由Ocelot API网关来传递Claims实现授权。
理论上讲,在引入Ocelot 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配置中,设置了以下几个参数:
此外,我们还使用了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的错误: