从 API 中的参考令牌获取用户 ID

Wil*_*ila 2 c# asp.net-core identityserver4 asp.net-core-2.0

我的设置,

  • 使用 MVC 身份存储用户的 IdentityServer,使用http://docs.identityserver.io/en/release/quickstarts/0_overview.html教程创建dotnet new mvc -au Individual并应用,在 localhost 5000 中运行。
  • 一个客户端应用程序,但现在我正在使用邮递员进行测试。
  • 使用 生成的 WEB API,dotnet new webapi在 localhost 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" }
        }
}
Run Code Online (Sandbox Code Playgroud)

密码和用户通过邮递员发送,收到的令牌也通过邮递员发送到 WEB API,类似于localhost:5001/v1/test使用粘贴在 option 中的令牌进行调用bearer token

在 API 启动中,在 ConfigureServices 中,我添加了以下几行

services.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication(options =>
    {
        options.Authority= "http://localhost:5000";
        options.ApiName= "api_resource";
        options.ApiSecret = "apiSecret";
    });
Run Code Online (Sandbox Code Playgroud)

我在控制器中获取用户的 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;
}
Run Code Online (Sandbox Code Playgroud)

问题本身是,我是否使用正确的路径从参考令牌中获取 Id?,因为现在它有效但我不想错过任何东西,特别是认为这是一个安全问题。

我也在问,因为我见过其他人使用

string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;

这更直接,但似乎不适合参考标记。

提前致谢。

Mas*_*ton 7

在受[Authorize]属性保护的控制器操作中,您可以直接从 中获取声明ClaimsPrinciple,而无需通过手动发现客户端。声明原则很容易与User您的控制器内部混淆。

我也在问,因为我见过其他人使用

string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;

这更直接,但似乎不适合参考标记。

它适用于参考令牌。访问sub声明应该没有问题。

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

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

或者

public static string GetEmail(this ClaimsPrincipal principal)
{
    return principal?.FindFirst(x => x.Type.Equals(JwtClaimTypes.Email))?.Value; 
}
Run Code Online (Sandbox Code Playgroud)

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

  • 显然,您只能获取最初请求的声明。我倾向于使用身份模型中包含的[JwtTokenTypes](https://github.com/IdentityModel/IdentityModel/blob/master/source/IdentityModel.Shared/JwtClaimTypes.cs),然后使用扩展方法如`public静态字符串 GetUserName(this ClaimsPrincipal principal) { return principal?.FindFirst(x =&gt; x.Type.Equals(JwtClaimTypes.Subject))?.Value; }`。所以我可以在控制器中调用`User.GetUserName()`。根据您的目的更改名称和声明类型。 (2认同)