无法根据 Identity Server 4 中的策略(一般授权)访问安全端点
Can't access secure endpoint under a policy (authorization in general works) in Identity Server 4
在我的 API 的 Startup.cs 中,我有以下授权策略。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication(...);
...
services.AddAuthorization(options =>
{
options.AddPolicy("VerySecurePolicy", policy =>
{
policy.RequireClaim("admin");
});
options.AddPolicy("VaguelySecurePolicy", policy =>
{
policy.RequireAuthenticatedUser();
});
});
}
然后,我保护了两种操作方法,一种是无参数属性,另一种是指定策略。
[Authorize, HttpGet("regular")]
public IActionResult GetRegularData() { return Ok("This is regular level data."); }
[Authorize(Policy = "VerySecurePolicy"), HttpGet("admin")]
public IActionResult GetAdminData() { return Ok("This is admin level data."); }
登录后可以访问前者,不能访问后者。我的推论是声明 admin 没有正确分配给我的用户,我看不到我遗漏了什么。使用我的访问令牌检查 user info endpoint (/connect/userinfo) 给我 ID、电子邮件等,但没有角色 admin.检查令牌本身根本没有显示声明数组(只有范围和通常的声明,如 sub、exp 等)。
这是登录的TestUser
实例。
yield return new TestUser
{
SubjectId = "37cfad39-e4da-486b-a8db-a752565125f8", ...
Claims = new List<Claim>
{
new Claim(JwtClaimTypes.Email, "fakey.uno@touchtech.comm"), ...
new Claim(JwtClaimTypes.Role, "admin")
}
};
其中一个 API scopes declared contains admin as a claim. I've verified that scope to be in the access token. I also added info in an API resource 就像这样(虽然我不确定它是否真的需要这样做)。
yield return new ApiScope("test_scope_a1", "Test scope A1", new[] { "admin" });
yield return new ApiResource
{
...
Scopes = new List<string> { "test_scope_a1", ... },
UserClaims = new List<string> { "admin", ... }
};
努力证明:
您的 VerySecurePolicy
政策设置不正确。像这样定义策略。注意 claimType
参数是 ClaimTypes.Role
,而不是 role
[1].
options.AddPolicy("VerySecurePolicy", policy => {
policy.RequireClaim(claimType: ClaimTypes.Role, "admin");
});
或者,如果这更合适,请使用其他 require 方法。
options.AddPolicy("VerySecurePolicy", policy => {
policy.RequireRole("admin");
});
有quite a few such methods,可以将它们链接起来进行复杂的安全定义。
policy.RequireAuthenticatedUser()
.RequireClaim("client_id", "spa_client")
.RequireRole("admin")
.RequireAssertion(context => context.User.Name == "Konrad");
});
您在原始样本中实际拥有的是 admin
索赔的支票,而不是 role: admin
索赔。
此外,您可以访问前一个操作,因为您没有指定策略,也没有 default/fallback 策略。这意味着 [Authorize]
仅确保用户已通过身份验证。
要使其正常工作,您需要明确指定它:
[Authorize("VaguelySecurePolicy"), HttpGet("admin")]
public IActionResult GetRegularData() { return Ok("This is regular level data."); }
或将其设置为 fallback/default 政策:
services.AddAuthorization(
options => {
options.AddPolicy("VerySecurePolicy", policy => { policy.RequireClaim("role", "admin"); });
//
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
}
);
这可确保带有 [Authorize]
注释的操作在没有特定策略集的情况下受回退策略的约束。
[1]:我们没有使用 role
声明类型,而是 its mapped equivalent http://schemas.microsoft.com/ws/2008/06/identity/claims/role
。如果您不希望发生此映射,并引用 JWT 中出现的声明,则需要禁用映射:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(...).AddJwtBearer(...);
现在 role
声明将显示为 role
,sub
将显示为 sub
,如您所料。
请注意,这会破坏一些事情,例如,您现在不能使用 User.IsInRole("rolename")
来检查角色,因为它期望角色声明类型为 ClaimTypes.Role
。所以我就让它保持原样。
参考资料
在我的 API 的 Startup.cs 中,我有以下授权策略。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication(...);
...
services.AddAuthorization(options =>
{
options.AddPolicy("VerySecurePolicy", policy =>
{
policy.RequireClaim("admin");
});
options.AddPolicy("VaguelySecurePolicy", policy =>
{
policy.RequireAuthenticatedUser();
});
});
}
然后,我保护了两种操作方法,一种是无参数属性,另一种是指定策略。
[Authorize, HttpGet("regular")]
public IActionResult GetRegularData() { return Ok("This is regular level data."); }
[Authorize(Policy = "VerySecurePolicy"), HttpGet("admin")]
public IActionResult GetAdminData() { return Ok("This is admin level data."); }
登录后可以访问前者,不能访问后者。我的推论是声明 admin 没有正确分配给我的用户,我看不到我遗漏了什么。使用我的访问令牌检查 user info endpoint (/connect/userinfo) 给我 ID、电子邮件等,但没有角色 admin.检查令牌本身根本没有显示声明数组(只有范围和通常的声明,如 sub、exp 等)。
这是登录的TestUser
实例。
yield return new TestUser
{
SubjectId = "37cfad39-e4da-486b-a8db-a752565125f8", ...
Claims = new List<Claim>
{
new Claim(JwtClaimTypes.Email, "fakey.uno@touchtech.comm"), ...
new Claim(JwtClaimTypes.Role, "admin")
}
};
其中一个 API scopes declared contains admin as a claim. I've verified that scope to be in the access token. I also added info in an API resource 就像这样(虽然我不确定它是否真的需要这样做)。
yield return new ApiScope("test_scope_a1", "Test scope A1", new[] { "admin" });
yield return new ApiResource
{
...
Scopes = new List<string> { "test_scope_a1", ... },
UserClaims = new List<string> { "admin", ... }
};
努力证明:
您的 VerySecurePolicy
政策设置不正确。像这样定义策略。注意 claimType
参数是 ClaimTypes.Role
,而不是 role
[1].
options.AddPolicy("VerySecurePolicy", policy => {
policy.RequireClaim(claimType: ClaimTypes.Role, "admin");
});
或者,如果这更合适,请使用其他 require 方法。
options.AddPolicy("VerySecurePolicy", policy => {
policy.RequireRole("admin");
});
有quite a few such methods,可以将它们链接起来进行复杂的安全定义。
policy.RequireAuthenticatedUser()
.RequireClaim("client_id", "spa_client")
.RequireRole("admin")
.RequireAssertion(context => context.User.Name == "Konrad");
});
您在原始样本中实际拥有的是 admin
索赔的支票,而不是 role: admin
索赔。
此外,您可以访问前一个操作,因为您没有指定策略,也没有 default/fallback 策略。这意味着 [Authorize]
仅确保用户已通过身份验证。
要使其正常工作,您需要明确指定它:
[Authorize("VaguelySecurePolicy"), HttpGet("admin")]
public IActionResult GetRegularData() { return Ok("This is regular level data."); }
或将其设置为 fallback/default 政策:
services.AddAuthorization(
options => {
options.AddPolicy("VerySecurePolicy", policy => { policy.RequireClaim("role", "admin"); });
//
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
}
);
这可确保带有 [Authorize]
注释的操作在没有特定策略集的情况下受回退策略的约束。
[1]:我们没有使用 role
声明类型,而是 its mapped equivalent http://schemas.microsoft.com/ws/2008/06/identity/claims/role
。如果您不希望发生此映射,并引用 JWT 中出现的声明,则需要禁用映射:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(...).AddJwtBearer(...);
现在 role
声明将显示为 role
,sub
将显示为 sub
,如您所料。
请注意,这会破坏一些事情,例如,您现在不能使用 User.IsInRole("rolename")
来检查角色,因为它期望角色声明类型为 ClaimTypes.Role
。所以我就让它保持原样。