c# - Ocelot RouteClaimsRequirement 无法识别我的声明并返回 403 Forbidden
问题描述
我已经在具有多个微服务的 Linux 容器中配置了 ocelot。为了限制我正在使用的一些微服务RouteClaimsRequirement
。我有管理员角色作为声明,但是当我使用角色管理员发送令牌时,Ocelot 返回 403 Forbidden,这是不符合RouteClaimsRequirement
. RouteClaimsRequirment
如果我从ocelot.json
一切工作中删除。
{
"DownstreamPathTemplate": "/api/v1/product/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "product",
"Port": 443
}
],
"UpstreamPathTemplate": "/product/{everything}",
"UpstreamHttpMethod": [ "Get", "Post", "Delete" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": []
},
"RouteClaimsRequirement": { <---- Problem Part
"Role": "Administrator"
},
"DangerousAcceptAnyServerCertificateValidator": true,
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "5s",
"PeriodTimespan": 6,
"Limit": 8
}
}
ocelot 项目启动类如下所示:
public void ConfigureServices(IServiceCollection services)
=> services
.AddCors()
.AddTokenAuthentication(Configuration)
.AddOcelot();
public static IServiceCollection AddTokenAuthentication(
this IServiceCollection services,
IConfiguration
configuration,
JwtBearerEvents events = null)
{
var secret = configuration
.GetSection(nameof(ApplicationSettings))
.GetValue<string>(nameof(ApplicationSettings.Secret));
var key = Encoding.ASCII.GetBytes(secret);
services
.AddAuthentication(authentication =>
{
authentication.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
authentication.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(bearer =>
{
bearer.RequireHttpsMetadata = false;
bearer.SaveToken = true;
bearer.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
if (events != null)
{
bearer.Events = events;
}
});
services.AddHttpContextAccessor();
services.AddScoped<ICurrentUserService, CurrentUserService>();
return services;
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app
.UseCors(options => options
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod())
.UseAuthentication()
.UseAuthorization()
.UseOcelot().Wait();
}
令牌生成如下所示:
public string GenerateToken(User user, IEnumerable<string> roles = null)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(this.applicationSettings.Secret);
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Name, user.Email)
};
if (roles != null)
{
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
}
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var encryptedToken = tokenHandler.WriteToken(token);
return encryptedToken;
}
解密令牌:
{
"nameid": "e18d5f1f-a315-435c-9e38-df9f2c77ad20",
"unique_name": "test@aa.bg",
"role": "Administrator",
"nbf": 1595460189,
"exp": 1596064989,
"iat": 1595460189
}
解决方案
经过对代码和调试的大量研究,我设法找到了问题所在。它来自声明名称,不是role
,而是http://schemas.microsoft.com/ws/2008/06/identity/claims/role
,但是当您将其写在 ocelot.json 中时,由于分号而被错误识别。
在研究它的中途,我在 github 中找到了一个解决相同问题的人的答案,我复制了他的解决方案。这里链接到解决方案。
问题是如何处理的:我们正在用特殊符号编写 url,:
然后我们将其重写为正确的,因此 ocelot.json 配置不会引发问题。
首先你需要创建IClaimsAuthoriser
public class ClaimAuthorizerDecorator : IClaimsAuthoriser
{
private readonly ClaimsAuthoriser _authoriser;
public ClaimAuthorizerDecorator(ClaimsAuthoriser authoriser)
{
_authoriser = authoriser;
}
public Response<bool> Authorise(ClaimsPrincipal claimsPrincipal, Dictionary<string, string> routeClaimsRequirement, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
{
var newRouteClaimsRequirement = new Dictionary<string, string>();
foreach (var kvp in routeClaimsRequirement)
{
if (kvp.Key.StartsWith("http$//"))
{
var key = kvp.Key.Replace("http$//", "http://");
newRouteClaimsRequirement.Add(key, kvp.Value);
}
else
{
newRouteClaimsRequirement.Add(kvp.Key, kvp.Value);
}
}
return _authoriser.Authorise(claimsPrincipal, newRouteClaimsRequirement, urlPathPlaceholderNameAndValues);
}
}
之后需要服务集合扩展:
public static class ServiceCollectionExtensions
{
public static IServiceCollection DecorateClaimAuthoriser(this IServiceCollection services)
{
var serviceDescriptor = services.First(x => x.ServiceType == typeof(IClaimsAuthoriser));
services.Remove(serviceDescriptor);
var newServiceDescriptor = new ServiceDescriptor(serviceDescriptor.ImplementationType, serviceDescriptor.ImplementationType, serviceDescriptor.Lifetime);
services.Add(newServiceDescriptor);
services.AddTransient<IClaimsAuthoriser, ClaimAuthorizerDecorator>();
return services;
}
}
在启动时,您在添加 ocelot 后定义扩展
public void ConfigureServices(IServiceCollection services)
{
services
.AddCors()
.AddTokenAuthentication(Configuration)
.AddOcelot().AddSingletonDefinedAggregator<DashboardAggregator>();
services.DecorateClaimAuthoriser();
}
最后,您需要将配置 json 更改为:
"RouteClaimsRequirement": {
"http$//schemas.microsoft.com/ws/2008/06/identity/claims/role": "Administrator"
}
推荐阅读
- android - 如何处理父视图上的触摸事件?
- java - 如何使用片段和顶点着色器使 2D 纹理表面不均匀?从纹理中删除某些像素?
- javascript - 为什么喜欢和不喜欢不能正常工作
- string - 打印两个字符串中相同且出现在相同位置的字符数
- sequelize.js - Sequelize 将 2 个查询组合为 sub where
- javascript - 提高计算两点之间距离的性能
- javascript - 0 谷歌脚本中谷歌表格中的前导数字格式
- javascript - npm update 不更新所有过时的模块
- r - 更改信息时查找唯一用户
- python - 如何使用调色板信息编写图像?