【问题标题】:Why RequiredScope attribute doesn't have any effect?为什么RequiredScope属性没有任何作用?
【发布时间】:2022-01-05 20:49:57
【问题描述】:

根据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

我在上次更新后遗漏了 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_token 标头

这里是 access_token 的主体

【问题讨论】:

  • 请将您的创业课程添加到您的问题中
  • 请参阅上面的更新。

标签: asp.net-core-webapi api-authorization


【解决方案1】:

你需要做的是在 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 只检查 scp 或 http://schemas.microsoft.com/identity/claims/scope 索赔。

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

【讨论】:

  • 感谢 Tore,但您是说这个 RequireScope 属性实际上是基于策略的授权的一部分,因此需要像 [Authorize(Policy = "MyPolicy")] 属性一样对待?即使是这样,如果我没有在 optionos.AddPolicy 中为它定义处理程序,访问不应该失败吗?我在这里有点困惑。
  • 我的意思是您还需要将 AddAuthorization()..... 添加到您的代码中,在我的代码中,它只是我在该主题中的一个培训课程中的一个示例。我更新了我的答案
  • 我对 [Authorize(Policy = "...")] 属性没有问题,因为这些是标准的基于策略的授权。是的,我使用 services.AddAuthorization 为它们添加处理程序,它们都很好用。我只是没有将它们包含在我的帖子中,因为它们在这里无关紧要。我唯一的问题是 [RequiredScope("...")] 属性总是被忽略。
  • 我没有在您的代码中看到这一点,所以这就是我在答案中添加它的原因。
  • 为什么不在这里使用策略而不是 [RequiredScope("AnyRandomName")]?如果政策有效,那么在任何地方都使用政策?策略比硬编码 requiredscope 更好用...
【解决方案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";
        }
    }
}

测试结果:

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

要保护 ASP.NET 或 ASP.NET Core Web API,您必须添加 [Authorize] 属性为下列项目之一:

如果您希望所有控制器操作都为控制器本身 受保护的 API 的单个控制器操作

this section's为例,

[RequiredScope("AnyRandomName")] 行之前添加[Authorize]

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

【讨论】:

  • 谢谢@Tiny。但事实并非如此。我在控制器级别和操作级别都尝试了您的建议,但在这两种情况下都没有任何区别。有什么想法吗?
  • 再次感谢。如上面我的更新所示,当我将我的代码与您的代码进行比较时,至少从我能说的情况来看,我没有看到任何东西跳出来。不知道为什么我的 RequireScope 属性一直被忽略。我在这里注意到的唯一区别是您使用的是 AzureAd,而我使用 IdentityServer4 作为 ID 提供程序。但这不重要,不是吗?
  • @Alexu 我觉得 azure ad 和 IdentityServer4 应该有一些区别,你关注的文档是关于 azure ad 的,而 identityServer4 可以参考this one
  • 再次感谢@Tiny 的投入!如果我理解正确,IdentityServer4 和 Azure Ad 在授权实现中都没有做任何事情——它们只发布安全令牌,并且基于这些,它是 ASP.Net Core 实现访问控制的实施。在我看来,我的问题是访问控制的一部分。我说的对吗?
  • 我不熟悉identityServer4,但是在azure ad上,确实提供了安全令牌,但也提供了访问控制。访问控制包含令牌解码并检查它是否过期,是否具有正确的角色或范围等,这些不是由您的代码编写的,而只是在您的方法或类之前添加一些注释。 @Alexu
【解决方案3】:

我们需要做的就是添加

services.AddRequiredScopeAuthorization();

为了让 RequireScopeAttrubute 正常工作,AddMicrosoftIdentityWebApiAuthentication 在后台所做的就是让它正常工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-11
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多