ASP.NET Core RequireClaim 具有多个作用域的“作用域”

8 c# jwt asp.net-core asp.net-core-2.1

我正在 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. 澄清一下,如果策略只需要一个 scope 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
Run Code Online (Sandbox Code Playgroud)

Nei*_*eil 8

您必须使用更复杂的索赔检查。使用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)
   );
});
Run Code Online (Sandbox Code Playgroud)

我写了一个小扩展方法,虽然不太容易阅读,但更容易使用:

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))
    );
}
Run Code Online (Sandbox Code Playgroud)