Ocelot RouteClaimsRequirement无法识别我的声明,返回403禁止访问。

4

我在Linux容器中配置了ocelot,并使用多个微服务。为了限制一些微服务,我使用。我拥有管理角色的声明,但是当我发送带有管理员角色的令牌时,Ocelot返回403 Forbidden,这是RouteClaimsRequirement中未满足条件的HttpCode。如果我从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
}
1个回答

6
经过大量的代码研究和调试,我成功找到了问题。它来自于声明名称,不是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"
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接