IsInRole return false 即使声明中有角色
IsInRole return false even if there is role in claims
我正在处理基于声明的身份验证,它运行良好。
现在我想添加角色授权。
我有用户的角色声明(例如 "Admin")
When the IsInRole() method is called, there is a check made to see if
the current user has that role. In claims-aware applications, the role
is expressed by a role claim type that should be available in the
token. The role claim type is expressed using the following URI:
http://schemas.microsoft.com/ws/2008/06/identity/claims/role
//Include all claims
//claims is List<Claim> with all claims
var id = new ClaimsIdentity(claims, "Cookies");
Request.GetOwinContext().Authentication.SignIn(id);
如果我检查用户是否在角色中,我会得到错误。虽然我有 "Admin" 值
的角色声明
User.IsInRole("Admin");
同时在我的 api 上授权属性将不起作用
[Authorize (Roles = "Admin")]
我可能无法理解如何使角色对用户可见。仅在声明列表中包含角色可能还不够?
可能,索赔的 ClaimType 只是 "role"。
您应该使用 Microsoft Schema 创建声明:
manager.AddClaim(dn1.Id, claim: new Claim(ClaimTypes.Role.ToString(), "ADMINISTRATOR"));
那么User.IsInRole("Admin");
和[Authorize (Roles = "Admin")]
就可以正常工作了。
这是因为 Microsoft Identity 使用架构:
http://schemas.microsoft.com/ws/2008/06/identity/claims/role
何时进行角色检查。
我建议您检查 ASPNETIdentity 数据库以全面了解如何插入声明。
我很确定 AspNetUserClaims 的 ClaimType 与 Microsoft Schema 不同。
此致
如果您的服务使用 Windows 身份验证,那么您收到的 IPrincipal.Identity
类型将是 WindowsPrincipal
。这有点误导,但是 WindowsPrincipal.IsInRole()
查找的 ClaimType
并不是您合理预期的 ClaimTypes.Role
,而是 ClaimTypes.GroupSid
。
但是,您不应假定当前身份用于指定角色的实际 ClaimType,因为不同类型的身份使用不同的值。相反,您应该参考 ClaimsIdentity.RoleClaimType
属性.
我们按照以下几行实施了 IAuthenticationFilter
:
public Task AuthenticateAsync(HttpAuthenticationContext context, cancellationToken)
{
var principal = context.Principal;
if(principal.Identity is ClaimsIdentity && principal.Identity.IsAuthenticated)
{
var ci = (ClaimsIdentity)principal.Identity;
// get the user's additional roles from somewhere and add Claims
ci.AddClaim(new Claim(ci.RoleClaimType, "MyRole"));
}
}
这允许我们在 ASP.Net 控制器中使用标准的 AuthorizeAttribute 机制。例如
[Authorize(Roles="MyRole")]
public IHttpActionResult Get()
{
//authenticated and authorised code here
}
有关进一步说明,请参阅 MSDN 上的 ClaimsIdentity.RoleClaimType。
请注意: 向 WindowsPrincipal
添加用户定义的角色可能会导致问题。似乎 .Net Framework 4.5 的当前实现(截至 2017 年 4 月)有时会在检查角色时抛出异常,期望角色的详细信息可从 Active Directory 获得。有关替代方法,请参阅 。
TL;DR 区分大小写,也许?
我发现默认使用的检查...
[Authorize(Roles = "RoleA,RoleB")]
...区分大小写。
我创建了混合大小写的角色,并使用了 AspNetCore 的身份管理器和非 EF 内存实现进行测试。
UserManager.IsInRole("RoleA") 返回 true,但是当通过 ClaimsPrincipal 检查时,HttpContext.User.IsInRole("RoleA") 返回 false。我将声明转储到文本中,可以看到有正确的 MS 模式的角色声明...
ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEA], Issuer:[TokenServer]
ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEB], Issuer:[TokenServer]
...但是声明值(角色)是大写的。
要解决这个问题,我只需要将属性更改为...
[Authorize(Roles = "ROLEA,ROLEB")]
...它奏效了。
因此,如果您在获取角色授权以在 AspNetCore 中工作时遇到问题,请尝试阅读声明,并准确匹配声明。您可以通过访问 HttpContext.User.Claims 对象来阅读声明...
foreach (var claim in HttpContext.User.Claims)
Console.WriteLine($"ClaimType:[{claim.Type}], ClaimValue:[{claim.Value}], Issuer:[{claim.Issuer}]");
当然可能是我以某种方式将角色编码为大写,或者在某处使用了 NormalisedRole,但您可能也做了同样的事情...
您没有提到您使用的是哪种身份验证方法,但是如果您使用的是 JWT 身份验证,那么您需要在生成令牌时将角色添加到 ClaimsIdentity,详见 post:ASP.NET Core JWT mapping role claims to ClaimsIdentity
注意
HttpContext.User.Identity.RoleClaimType: "角色"
可能与
ClaimTypes.Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
因此,在生成声明标识时,您可能需要使用“角色”作为键而不是 ClaimTypes 常量来添加声明。 ClaimsIdentity.IsInRole(String) 使用由 ClaimsIdentity.RoleClaimType.
定义的声明密钥
我的工厂代码是这样的...
var identity = await base.GenerateClaimsAsync(user);
var roles = await UserManager.GetRolesAsync(user);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
identity.AddClaim(new Claim("role", role));
}
return identity;
第一个添加确实是多余的,但让我觉得我实际上是在添加正确的声明。
我正在处理基于声明的身份验证,它运行良好。 现在我想添加角色授权。 我有用户的角色声明(例如 "Admin")
When the IsInRole() method is called, there is a check made to see if the current user has that role. In claims-aware applications, the role is expressed by a role claim type that should be available in the token. The role claim type is expressed using the following URI: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
//Include all claims
//claims is List<Claim> with all claims
var id = new ClaimsIdentity(claims, "Cookies");
Request.GetOwinContext().Authentication.SignIn(id);
如果我检查用户是否在角色中,我会得到错误。虽然我有 "Admin" 值
的角色声明User.IsInRole("Admin");
同时在我的 api 上授权属性将不起作用
[Authorize (Roles = "Admin")]
我可能无法理解如何使角色对用户可见。仅在声明列表中包含角色可能还不够?
可能,索赔的 ClaimType 只是 "role"。
您应该使用 Microsoft Schema 创建声明:
manager.AddClaim(dn1.Id, claim: new Claim(ClaimTypes.Role.ToString(), "ADMINISTRATOR"));
那么User.IsInRole("Admin");
和[Authorize (Roles = "Admin")]
就可以正常工作了。
这是因为 Microsoft Identity 使用架构:
http://schemas.microsoft.com/ws/2008/06/identity/claims/role
何时进行角色检查。 我建议您检查 ASPNETIdentity 数据库以全面了解如何插入声明。 我很确定 AspNetUserClaims 的 ClaimType 与 Microsoft Schema 不同。
此致
如果您的服务使用 Windows 身份验证,那么您收到的 IPrincipal.Identity
类型将是 WindowsPrincipal
。这有点误导,但是 WindowsPrincipal.IsInRole()
查找的 ClaimType
并不是您合理预期的 ClaimTypes.Role
,而是 ClaimTypes.GroupSid
。
但是,您不应假定当前身份用于指定角色的实际 ClaimType,因为不同类型的身份使用不同的值。相反,您应该参考 ClaimsIdentity.RoleClaimType
属性.
我们按照以下几行实施了 IAuthenticationFilter
:
public Task AuthenticateAsync(HttpAuthenticationContext context, cancellationToken)
{
var principal = context.Principal;
if(principal.Identity is ClaimsIdentity && principal.Identity.IsAuthenticated)
{
var ci = (ClaimsIdentity)principal.Identity;
// get the user's additional roles from somewhere and add Claims
ci.AddClaim(new Claim(ci.RoleClaimType, "MyRole"));
}
}
这允许我们在 ASP.Net 控制器中使用标准的 AuthorizeAttribute 机制。例如
[Authorize(Roles="MyRole")]
public IHttpActionResult Get()
{
//authenticated and authorised code here
}
有关进一步说明,请参阅 MSDN 上的 ClaimsIdentity.RoleClaimType。
请注意: 向 WindowsPrincipal
添加用户定义的角色可能会导致问题。似乎 .Net Framework 4.5 的当前实现(截至 2017 年 4 月)有时会在检查角色时抛出异常,期望角色的详细信息可从 Active Directory 获得。有关替代方法,请参阅
TL;DR 区分大小写,也许?
我发现默认使用的检查...
[Authorize(Roles = "RoleA,RoleB")]
...区分大小写。
我创建了混合大小写的角色,并使用了 AspNetCore 的身份管理器和非 EF 内存实现进行测试。
UserManager.IsInRole("RoleA") 返回 true,但是当通过 ClaimsPrincipal 检查时,HttpContext.User.IsInRole("RoleA") 返回 false。我将声明转储到文本中,可以看到有正确的 MS 模式的角色声明...
ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEA], Issuer:[TokenServer]
ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEB], Issuer:[TokenServer]
...但是声明值(角色)是大写的。
要解决这个问题,我只需要将属性更改为...
[Authorize(Roles = "ROLEA,ROLEB")]
...它奏效了。
因此,如果您在获取角色授权以在 AspNetCore 中工作时遇到问题,请尝试阅读声明,并准确匹配声明。您可以通过访问 HttpContext.User.Claims 对象来阅读声明...
foreach (var claim in HttpContext.User.Claims)
Console.WriteLine($"ClaimType:[{claim.Type}], ClaimValue:[{claim.Value}], Issuer:[{claim.Issuer}]");
当然可能是我以某种方式将角色编码为大写,或者在某处使用了 NormalisedRole,但您可能也做了同样的事情...
您没有提到您使用的是哪种身份验证方法,但是如果您使用的是 JWT 身份验证,那么您需要在生成令牌时将角色添加到 ClaimsIdentity,详见 post:ASP.NET Core JWT mapping role claims to ClaimsIdentity
注意 HttpContext.User.Identity.RoleClaimType: "角色"
可能与 ClaimTypes.Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
因此,在生成声明标识时,您可能需要使用“角色”作为键而不是 ClaimTypes 常量来添加声明。 ClaimsIdentity.IsInRole(String) 使用由 ClaimsIdentity.RoleClaimType.
定义的声明密钥我的工厂代码是这样的...
var identity = await base.GenerateClaimsAsync(user);
var roles = await UserManager.GetRolesAsync(user);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
identity.AddClaim(new Claim("role", role));
}
return identity;
第一个添加确实是多余的,但让我觉得我实际上是在添加正确的声明。