如何在用户声明列表中保留多个 ClaimTypes.Role 值?
How do I persist multiple ClaimTypes.Role values in User's Claims list?
我正在构建一个 ASP.NET 核心 (v3.1) 模块,我刚刚设法配置了一个 OpenIdConnect 身份验证。现在,我需要从 API 中获取所有用户的角色,以便授予或拒绝对它们的访问权限,然后我通过 OnAuthorizationCodeReceived
像这样的事件:
OnAuthorizationCodeReceived = async (context) =>
{
// Uses the authentication code and gets the access and refresh token
var client = new HttpClient();
var response = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest()
{
Address = urlServer + "/connect/token",
ClientId = "hybrid",
Code = context.TokenEndpointRequest.Code,
RedirectUri = context.TokenEndpointRequest.RedirectUri,
}
if (response.IsError) throw new Exception(response.Error);
var identity = new ClaimsIdentity(context.Principal.Identity);
var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
listRoles.ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role)));
context.HttpContext.User = new ClaimsPrincipal(identity);
context.HandleCodeRedemption(response.AccessToken, response.IdentityToken);
}
调试时,我注意到所有角色都添加到此行之后的用户声明列表中:
context.HttpContext.User = new ClaimsPrincipal(identity);
但是,显然,在我的 Home 控制器中(即用户在经过身份验证后被重定向到的地方),当我访问 HttpContext.User 时,我似乎找不到我添加的任何角色之前除了 "Admin"(我猜这是默认的 ClaimTypes.Role 值)。
[Authorize]
public IActionResult Index()
{
if (User.IsInRole("SomeRole"))
{
return RedirectToAction("SomeAction", "SomeController");
}
else
{
return RedirectToAction("Forbidden", "Error");
}
}
阅读其他一些帖子论坛和主题,我发现这可能是一个上下文持久性问题,我试图在我的帐户控制器中使用以下代码解决这个问题:
public async Task Login(string returnUrl = "/")
{
await HttpContext.ChallengeAsync(
"OIDC",
new AuthenticationProperties
{
AllowRefresh = false,
IsPersistent = true,
RedirectUri = returnUrl
});
}
一些示例说我可以使用 context.Principal.AddIdentity(identity);
来保留新的声明列表,但随后出现以下错误:
InvalidOperationException: only a single identity supported
IdentityServer4.Hosting.IdentityServerAuthenticationService.AssertRequiredClaims(ClaimsPrincipal principal)
总而言之,我必须找到一种方法来保留我添加到用户声明列表中的角色声明,但直到现在我都没有成功。
更新一下,如果这对任何人都有用的话。
我推断问题出在 context.HttpContext.User = new ClaimsPrincipal(identity);
,我之前认为这是处理新声明持久性的代码的一部分。
实际上,我确实注意到有一个 context.Principal
类型的 ClaimsPrincipal
属性,看起来它是实际的当前上下文,所以我深入研究它并试图找出一种方法将元素添加到其只读的“IEnumerable<Claim> Claims
”属性。
一段时间后,我发现以下解决方案对我来说效果很好:
而不是
var identity = new ClaimsIdentity(context.Principal.Identity);
var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
listRoles.ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role)));
我试过了
var identity = context.Principal.Identity as ClaimsIdentity;
if(identity != null)
{
var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
foreach (var role in listRoles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
我正在构建一个 ASP.NET 核心 (v3.1) 模块,我刚刚设法配置了一个 OpenIdConnect 身份验证。现在,我需要从 API 中获取所有用户的角色,以便授予或拒绝对它们的访问权限,然后我通过 OnAuthorizationCodeReceived
像这样的事件:
OnAuthorizationCodeReceived = async (context) =>
{
// Uses the authentication code and gets the access and refresh token
var client = new HttpClient();
var response = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest()
{
Address = urlServer + "/connect/token",
ClientId = "hybrid",
Code = context.TokenEndpointRequest.Code,
RedirectUri = context.TokenEndpointRequest.RedirectUri,
}
if (response.IsError) throw new Exception(response.Error);
var identity = new ClaimsIdentity(context.Principal.Identity);
var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
listRoles.ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role)));
context.HttpContext.User = new ClaimsPrincipal(identity);
context.HandleCodeRedemption(response.AccessToken, response.IdentityToken);
}
调试时,我注意到所有角色都添加到此行之后的用户声明列表中:
context.HttpContext.User = new ClaimsPrincipal(identity);
但是,显然,在我的 Home 控制器中(即用户在经过身份验证后被重定向到的地方),当我访问 HttpContext.User 时,我似乎找不到我添加的任何角色之前除了 "Admin"(我猜这是默认的 ClaimTypes.Role 值)。
[Authorize]
public IActionResult Index()
{
if (User.IsInRole("SomeRole"))
{
return RedirectToAction("SomeAction", "SomeController");
}
else
{
return RedirectToAction("Forbidden", "Error");
}
}
阅读其他一些帖子论坛和主题,我发现这可能是一个上下文持久性问题,我试图在我的帐户控制器中使用以下代码解决这个问题:
public async Task Login(string returnUrl = "/")
{
await HttpContext.ChallengeAsync(
"OIDC",
new AuthenticationProperties
{
AllowRefresh = false,
IsPersistent = true,
RedirectUri = returnUrl
});
}
一些示例说我可以使用 context.Principal.AddIdentity(identity);
来保留新的声明列表,但随后出现以下错误:
InvalidOperationException: only a single identity supported
IdentityServer4.Hosting.IdentityServerAuthenticationService.AssertRequiredClaims(ClaimsPrincipal principal)
总而言之,我必须找到一种方法来保留我添加到用户声明列表中的角色声明,但直到现在我都没有成功。
更新一下,如果这对任何人都有用的话。
我推断问题出在 context.HttpContext.User = new ClaimsPrincipal(identity);
,我之前认为这是处理新声明持久性的代码的一部分。
实际上,我确实注意到有一个 context.Principal
类型的 ClaimsPrincipal
属性,看起来它是实际的当前上下文,所以我深入研究它并试图找出一种方法将元素添加到其只读的“IEnumerable<Claim> Claims
”属性。
一段时间后,我发现以下解决方案对我来说效果很好:
而不是
var identity = new ClaimsIdentity(context.Principal.Identity);
var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
listRoles.ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role)));
我试过了
var identity = context.Principal.Identity as ClaimsIdentity;
if(identity != null)
{
var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
foreach (var role in listRoles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
}