为什么 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
无论如何都会在后台执行此操作以使其正常工作。
根据 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
无论如何都会在后台执行此操作以使其正常工作。