关于 c#:如何在用户的声明列表中保留多个 ClaimTypes.Role 值?

How do I persist multiple ClaimTypes.Role values in User's Claims list?

我正在构建一个 ASP.NET Core (v3.1) 模块,并且我刚刚设法配置了一个 OpenIdConnect 身份验证。现在,我需要从 API 获取所有用户的角色,以便授予或拒绝对它们的访问权限,然后我通过 OnAuthorizationCodeReceived 事件向用户声明列表中的同一个声明角色"ClaimTypes.Role"添加了多个声明值,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
}

在调试时,我注意到所有角色都添加到此行之后的用户声明列表中:

1
context.HttpContext.User = new ClaimsPrincipal(identity);

但是,显然,在我的 Home 控制器中(这是用户在经过身份验证后被重定向到的地方),当我访问 HttpContext.User 时,我似乎找不到我之前添加的任何角色,除了" Admin"(我猜是默认的 ClaimTypes.Role 值)。

1
2
3
4
5
6
7
8
9
10
11
12
[Authorize]
public IActionResult Index()
{
    if (User.IsInRole("SomeRole"))
    {
        return RedirectToAction("SomeAction","SomeController");
    }
    else
    {
        return RedirectToAction("Forbidden","Error");
    }
}

阅读其他一些帖子论坛和主题,我发现这可能是一个上下文持久性问题,我试图在我的帐户控制器中使用此代码解决:

1
2
3
4
5
6
7
8
9
10
11
public async Task Login(string returnUrl ="/")
{
    await HttpContext.ChallengeAsync(
       "OIDC",
        new AuthenticationProperties
        {
            AllowRefresh = false,
            IsPersistent = true,
            RedirectUri = returnUrl
        });
}

一些例子说我可以使用 context.Principal.AddIdentity(identity); 来保留新的 Claims 列表,但是我得到了以下错误:

1
2
InvalidOperationException: only a single identity supported
IdentityServer4.Hosting.IdentityServerAuthenticationService.AssertRequiredClaims(ClaimsPrincipal principal)

总而言之,我必须找到一种方法来保留我添加到用户声明列表中的角色声明,但直到现在我都没有成功。


更新一下,如果这对任何人有用。

我推断出 context.HttpContext.User = new ClaimsPrincipal(identity); 的问题,我之前理解它是处理新声明持久性的代码的一部分。

实际上,我确实注意到有一个 ClaimsPrincipal 类型的 context.Principal 属性,看起来它是实际的 Current 上下文,所以我深入研究它并试图找出一种方法将元素添加到它的 "IEnumerable<Claim> Claims" 只读属性。

一段时间后,我发现以下解决方案对我来说效果很好:

代替

1
2
3
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)));

我试过

1
2
3
4
5
6
7
8
9
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));
    }
}