ASP.NET Core RequireClaim «область действия» с несколькими областями действия - PullRequest
0 голосов
/ 24 февраля 2019

Я разрабатываю систему авторизации на ASP.NET Core 2.1, которая требует как уровня ресурсов, так и соблюдения области действия перед предоставлением доступа.То есть я должен быть автором книги (их может быть несколько), и у меня должны быть необходимые области («write.book», «read.book», «delete.book» и т. Д.).Я успешно настроил JWT в Startup.cs и получаю 401s, когда передаются недопустимые токены.Проблема, с которой я сталкиваюсь, заключается в обеспечении соблюдения границ.policy.RequireClaim("scope", "write.book") работает, когда токену доступа требуется только одна требуемая область, но всегда происходит сбой, токен доступа содержит несколько областей действия "write.book delete.book".Как настроить политику так, чтобы ей требовался список областей, которые могут быть подмножеством областей, содержащихся в маркере доступа?Я не вижу никаких Policy методов, которые принимают список областей, поэтому я предполагаю, что фреймворк просто выполняет сравнение строк, поэтому авторизация не удалась.write.book != write.book delete.book.Чтобы уточнить, если для политики требуется только одна область действия write.book, но в токене доступа write.book delete.book присутствует несколько элементов, авторизация завершится неудачей.

Приведенный ниже код работает, только если токен доступа содержит одну область действия, ине работает, если присутствует несколько.

authorization.AddPolicy("writeBookPolicy", policy => {
    policy.RequireAuthenticatedUser().AddAuthenticationSchemes("Bearer")
      .RequireClaim("scope", "write.book").Build();
});

{"scope": "write.book"} // Works
{"scope": "write.book delete.book"} //Fails

1 Ответ

0 голосов
/ 20 августа 2019

Вы должны использовать более сложную проверку претензий.Вместо этого используйте RequireAssertion() и проанализируйте утверждение объема:

var scopes = new[] { "write.book", "delete.book" };
builder.RequireAssertion(context => {
   var claim = context.User.FindFirst("scope");
   if(claim == null) { return false; }
   return claim.Value.Split(' ').Any(s =>
      scopes.Contains(s, StringComparer.OrdinalIgnoreCase)
   );
});

Я написал небольшой метод расширения, который, хотя и менее прост для чтения, проще в использовании:

private static readonly IEnumerable<string> _scopeClaimTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
    "http://schemas.microsoft.com/identity/claims/scope",
    "scope"
};
public static AuthorizationPolicyBuilder RequireScope(this AuthorizationPolicyBuilder builder, params string[] scopes) {
    return builder.RequireAssertion(context =>
        context.User
            .Claims
            .Where(c => _scopeClaimTypes.Contains(c.Type))
            .SelectMany(c => c.Value.Split(' '))
            .Any(s => scopes.Contains(s, StringComparer.OrdinalIgnoreCase))
    );
}
...