C# 从API中的引用令牌获取用户Id

C# 从API中的引用令牌获取用户Id,c#,asp.net-core,identityserver4,asp.net-core-2.0,C#,Asp.net Core,Identityserver4,Asp.net Core 2.0,我的设置 使用MVC标识存储用户的Identity Server,使用dotnet new MVC-au Individual创建,并应用教程,在localhost 5000中运行 客户端应用程序,但现在我使用postman进行测试 使用dotnet new webapi创建的WEB API,在本地主机5001中运行 IdentityServer资源和客户端配置如下所示,请注意,我使用的是引用令牌: public static IEnumerable<IdentityResource&

我的设置

  • 使用MVC标识存储用户的Identity Server,使用
    dotnet new MVC-au Individual创建,并应用教程,在localhost 5000中运行
  • 客户端应用程序,但现在我使用postman进行测试
  • 使用
    dotnet new webapi
    创建的WEB API,在本地主机5001中运行
IdentityServer资源和客户端配置如下所示,请注意,我使用的是引用令牌:

public static IEnumerable<IdentityResource> GetIdentityResources() {
    return new List<IdentityResource>{ new IdentityResources.OpenId() };
}

public static IEnumerable<ApiResource> GetApiResources() {
    return new List<ApiResource>{
        new ApiResource("api_resource", "API Resource") {
            Description= "API Resource Access",
            ApiSecrets= new List<Secret> { new Secret("apiSecret".Sha256()) },
        }
    };
}

public static IEnumerable<Client> GetClients() {
    return new List<Client>{
        new Client {
            ClientId= "angular-client",
            ClientSecrets= { new Secret("secret".Sha256()) },
            AllowedGrantTypes= GrantTypes.ResourceOwnerPassword,
            AllowOfflineAccess= true,
            AccessTokenType = AccessTokenType.Reference,
            AlwaysIncludeUserClaimsInIdToken= true,
            AllowedScopes= { "api_resource" }
        }
}
我得到控制器内用户的Id,如下所示:

public async Task<IActionResult> Get(int id) {
    var discoveryClient = new DiscoveryClient("http://localhost:5000");
        var doc = await discoveryClient.GetAsync();


        var introspectionClient = new IntrospectionClient(
            doc.IntrospectionEndpoint,
            "api_resource",
            "apiSecret");

        var token= await HttpContext.GetTokenAsync("access_token");

        var response = await introspectionClient.SendAsync(
            new IntrospectionRequest { Token = token });

        var userId = response.Claims.Single(c => c.Type == "sub").Value;
}
公共异步任务Get(int-id){
var discoveryClient=新的discoveryClient(“http://localhost:5000");
var doc=await discoveryClient.GetAsync();
var introspectionClient=新的introspectionClient(
内省点博士,
“api_资源”,
“机密”);
var token=await HttpContext.GetTokenAsync(“访问令牌”);
var response=await introspectionClient.sendsync(
新的内省请求{Token=Token});
var userId=response.Claims.Single(c=>c.Type==“sub”).Value;
}
问题本身是,我是否使用正确的路径从引用令牌获取Id?,因为现在它可以工作,但我不想错过任何东西,特别是考虑到这是一个安全问题

我这么问也是因为我看到其他人在使用

stringuserid=User.Claims.FirstOrDefault(c=>c.Type==ClaimTypes.NameIdentifier).Value

这更简单,但似乎不适合引用标记


提前感谢。

在受
[Authorize]
属性保护的控制器操作中,您只需直接从
ClaimsPrinciple
获取索赔,而无需通过手动查找客户端。索赔原则简单地用控制器中的
User
来表示

我这么问也是因为我看到其他人在使用

字符串userId=User.Claims.FirstOrDefault(c=>c.Type== ClaimTypes.NameIdentifier)值

这更直截了当,但似乎与参考不符 代币

它与引用标记一起工作很好。访问
sub
索赔时应该没有问题

编辑: 正如我在下面的评论中提到的,我倾向于使用标准并在
ClaimsPrinciple
上创建一些扩展方法,例如:

public static string GetSub(this ClaimsPrincipal principal)
{
    return principal?.FindFirst(x => x.Type.Equals(JwtClaimTypes.Subject))?.Value;
}

。。。因此,在受保护的操作中,我可以简单地使用
User.GetEmail()
来获取索赔值。
值得一提的是,任何检索索赔值的方法都只有在索赔实际存在的情况下才有效。i、 e.请求ZoneInfo声明将不起作用,除非该声明首先作为令牌请求的一部分被请求。

Sub或subject是用户id。您可以使用JwtClaimTypes.subject、ClaimTypes.NameIdentifier,有时甚至我认为这取决于令牌的解析方式。但是,如果sub起作用,那么您所做的就可以了。使用
ClaimTypes.NameIdentifier
会给我
System.InvalidOperationException:序列不包含匹配的元素
,而是使用
“sub”
,显然您只能得到最初请求的索赔。我倾向于使用标识模型中包含的,然后使用扩展方法,例如
公共静态字符串GetUserName(this ClaimsPrincipal principal){return principal?.FindFirst(x=>x.Type.Equals(JwtClaimTypes.Subject))?.Value;}
。所以我可以在控制器中调用
User.GetUserName()
。为您的目的更改名称和索赔类型。@Mashton请将此作为独立答案添加!:)
public static string GetSub(this ClaimsPrincipal principal)
{
    return principal?.FindFirst(x => x.Type.Equals(JwtClaimTypes.Subject))?.Value;
}
public static string GetEmail(this ClaimsPrincipal principal)
{
    return principal?.FindFirst(x => x.Type.Equals(JwtClaimTypes.Email))?.Value; 
}