Ocelot RouteClaimsRequirement 无法识别我的声明并返回 403 Forbidden

问题描述 投票:0回答:1

我已经在具有多个微服务的Linux容器中配置了ocelot。为了限制我正在使用的一些微服务

RouteClaimsRequirement
。我有管理员角色作为声明,但是当我使用角色管理员发送令牌时,Ocelot 返回 403 Forbidden,这是 not 满足
RouteClaimsRequirement
中的标准的 HttpCode。如果我从
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": "[email protected]",
  "role": "Administrator",
  "nbf": 1595460189,
  "exp": 1596064989,
  "iat": 1595460189
}
c# asp.net-core ocelot
1个回答
7
投票

经过大量的代码研究和调试,我终于找到了问题所在。它来自索赔名称,不是

role
,而是
http://schemas.microsoft.com/ws/2008/06/identity/claims/role
,但是当你把它写在
ocelot.json
中时,它会因为冒号(
:
)而被错误识别。

研究中途,我在Github上找到了一个解决同样问题的人的答案,我复制了他的解决方案。这是解决方案的链接。

问题如何处理:我们使用特殊符号而不是

:
编写URL,然后将其重写为正确的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"
  }
© www.soinside.com 2019 - 2024. All rights reserved.