为什么 RequiredScope 属性没有任何作用?

Why RequiredScope attribute doesn't have any effect?

根据 this Microsoft document,您应该能够将 [RequiredScope("SomeScopeName")] 之类的属性应用于控制器级别或操作级别以保护 API。但是当我在我的 API 中尝试它时,它似乎根本没有任何效果 - 无论我使用什么范围名称(我确保我在令牌中没有该名称的范围),我总是直接进入我应该失败的 API 行动。但与此同时,我的策略属性(例如 [Authorize(Policy = "PolicyName")])工作得很好。我错过了什么?

[ApiController]
[RequiredScope("AnyRandomName")]
public class MyApiController : ControllerBase
{

更新

这是我的 Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        IdentityModelEventSource.ShowPII = true; 
        services.AddControllers();

        services.AddSwaggerGen(opt =>
        {
            opt.CustomSchemaIds(type => type.ToString() + type.GetHashCode()); 
        });

        services.Configure<HostOptions>(Configuration.GetSection(HostOptions.HOST));

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); 
        JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
        services.AddAuthentication("Bearer").AddJwtBearer(options =>
        {
            options.Authority = Configuration[HostOptions.IDENTITYGATEWAY];
            options.SaveToken = true;
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false
            };
        });

        services.AddTransient<gRPCServiceHelper>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseExceptionHandler("/error-local-development");
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "GroupDemographicEFCore v1"));
        }
        else
        {
            app.UseExceptionHandler("/error");
        }

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

这是我的 API 控制器

[ApiController]
[Authorize]
[RequiredScope("NoSuchScope")]
public class MyApiController : ControllerBase
{
    public MyApiController([NotNull] IConfiguration configuration, [NotNull] ILogger<MyApiController> logger,
        [NotNull] gRPCServiceHelper helper) : base(configuration, logger, helper)
    {
    }

    [HttpGet]
    [Route("/clients/summary")]
    public async Task<IActionResult> ClientsSummaryGet()
    {
        ...

请注意,我在控制器级别应用了此处的属性。但是,如果我将它们移至操作级别,则没有任何区别 - RequiredScope 属性总是被忽略。

UPDATE-1

我在上次 post 更新中遗漏了 AddAuthorization,因为我认为它与我这里的问题无关。我现在用我使用的一些策略将其添加回来。再一次,这些政策都运作良好,我看不出这与我遇到的问题有什么关系。

services.AddAuthorization(options =>
{
    options.AddPolicy("OperatorCode", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("OperatorCode");
    });
    options.AddPolicy("OperatorCode:oprtr0", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("OperatorCode", "oprtr0");
    });
    options.AddPolicy("Role:User+OperatorCode:oprtr0", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireRole("User");
        policy.RequireClaim("OperatorCode", "oprtr0");
    });
    options.AddPolicy("Role:Admin||Role:User", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireRole("Admin", "User");
    });
});

这里是access_tokenheader

这里是 access_token 的 body

我的代码:

正在安装这 2 个软件包:

<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="4.5.1" />
<PackageReference Include="Microsoft.Identity.Web" Version="1.21.1" />

Startup.cs,在ConfigureServices方法中添加代码。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd");
    services.AddControllers();
}

不要忘记Configure方法中的这两行:

app.UseAuthentication();
app.UseAuthorization();

我的测试控制器:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;
using System.Collections.Generic;

namespace WebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    [RequiredScope("User.Read")]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1", "value2" };
        }

        [HttpPost]
        public string getRes() {
            return "hello world";
        }
    }
}

测试结果:

============================================= =================

To protect an ASP.NET or ASP.NET Core web API, you must add the [Authorize] attribute to one of the following items:

The controller itself if you want all controller actions to be protected The individual controller action for your API

根据this section's例子,

在行 [RequiredScope("AnyRandomName")] ?

之前添加 [Authorize]
[ApiController]
[Authorize]
[RequiredScope("AnyRandomName")]
public class MyApiController : ControllerBase
{

你需要做的是在Startup.cs中添加和配置授权,像这样:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {

        options.AddPolicy("ViewReports", policy =>
                          policy.RequireAuthenticatedUser()
                                .RequireRole("Finance")
                                .RequireRole("Management")
                          );                  
    });

策略规定用户必须经过身份验证并处于两个 角色。在此示例中,RequireAuthenticatedUser() 是可选的。

然后您可以像这样使用该政策:

[Authorize(Policy = "ViewReports")]
public IActionResult ViewReports()
{
    return View();
}

要使角色声明生效,您必须在令牌中定义角色声明的名称,方法是:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
     .AddJwtBearer(options =>
     {
           options.TokenValidationParameters.NameClaimType = "name";
           options.TokenValidationParameters.RoleClaimType = "role";
     });

否则可能找不到角色,因为 OpenIDConnect 和 Microsoft 对声明的名称有不同的看法。

在较长的 运行 中,使用策略会给你更好更清晰的代码,因为如果你将来需要更改范围,你需要更新所有控制器 类。使用策略,您可以在一个地方更改它。

此外,根据 GitHub 上的 issue,它说:

RequiredScopes just checks at the scp or http://schemas.microsoft.com/identity/claims/scope claims.

这意味着您可能需要进行一些声明转换(重命名)以使 RequiredScope 映射到访问令牌中的范围声明。

我们需要做的就是添加

services.AddRequiredScopeAuthorization();

要使 RequireScopeAttrubute 正常工作,AddMicrosoftIdentityWebApiAuthentication 无论如何都会在后台执行此操作以使其正常工作。