从 API 中的引用令牌获取用户 ID
Get user Id from reference token in API
我的设置,
- 使用 MVC 身份存储用户的 IdentityServer,使用
dotnet new mvc -au Individual
创建并应用 http://docs.identityserver.io/en/release/quickstarts/0_overview.html 教程,运行 在本地主机 5000 中。
- 一个客户端,不过我现在是用postman做测试
- 一个 WEB API,在本地主机 5001 中使用
dotnet new webapi
、运行 创建。
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" }
}
}
密码和用户通过邮递员发送,收到的令牌也通过邮递员发送到 WEB API,类似于调用 localhost:5001/v1/test
并在选项 bearer token
中粘贴令牌.
在 API 启动中,我在 ConfigureServices 中添加以下行
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority= "http://localhost:5000";
options.ApiName= "api_resource";
options.ApiSecret = "apiSecret";
});
我在控制器中获取用户的 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;
}
问题本身是,我是否使用正确的路径从引用令牌中获取 ID?,因为现在它可以工作,但我不想错过任何东西,特别认为这是一个安全问题。
我问也是因为我看到其他人使用
string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
这更直接,但似乎不适合参考标记。
提前致谢。
在受 [Authorize]
属性保护的控制器操作中,您可以直接从 ClaimsPrinciple
获取声明,而无需通过手动发现客户端。 claims 原则在你的控制器中很方便地使用 User
作为别名。
I'm asking also because I have seen anothers using
string userId = User.Claims.FirstOrDefault(c => c.Type ==
ClaimTypes.NameIdentifier).Value;
that is more straightforward but doesn't seems to fit with reference
tokens.
它适用于参考标记。您访问 sub
声明应该没有问题。
编辑:
正如我在下面的评论中提到的,我倾向于使用标准 JwtClaimTypes 并在 ClaimsPrinciple
上创建一些扩展方法,例如:
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;
}
... 这样在我受保护的操作中我可以简单地使用 User.GetEmail()
来获取声明值。
值得一提的是,任何检索声明值的方法只有在声明实际存在时才有效。即要求 ZoneInfo 声明将不起作用,除非该声明首先作为令牌请求的一部分被请求。
我的设置,
- 使用 MVC 身份存储用户的 IdentityServer,使用
dotnet new mvc -au Individual
创建并应用 http://docs.identityserver.io/en/release/quickstarts/0_overview.html 教程,运行 在本地主机 5000 中。 - 一个客户端,不过我现在是用postman做测试
- 一个 WEB API,在本地主机 5001 中使用
dotnet new webapi
、运行 创建。
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" }
}
}
密码和用户通过邮递员发送,收到的令牌也通过邮递员发送到 WEB API,类似于调用 localhost:5001/v1/test
并在选项 bearer token
中粘贴令牌.
在 API 启动中,我在 ConfigureServices 中添加以下行
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority= "http://localhost:5000";
options.ApiName= "api_resource";
options.ApiSecret = "apiSecret";
});
我在控制器中获取用户的 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;
}
问题本身是,我是否使用正确的路径从引用令牌中获取 ID?,因为现在它可以工作,但我不想错过任何东西,特别认为这是一个安全问题。
我问也是因为我看到其他人使用
string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
这更直接,但似乎不适合参考标记。
提前致谢。
在受 [Authorize]
属性保护的控制器操作中,您可以直接从 ClaimsPrinciple
获取声明,而无需通过手动发现客户端。 claims 原则在你的控制器中很方便地使用 User
作为别名。
I'm asking also because I have seen anothers using
string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
that is more straightforward but doesn't seems to fit with reference tokens.
它适用于参考标记。您访问 sub
声明应该没有问题。
编辑:
正如我在下面的评论中提到的,我倾向于使用标准 JwtClaimTypes 并在 ClaimsPrinciple
上创建一些扩展方法,例如:
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;
}
... 这样在我受保护的操作中我可以简单地使用 User.GetEmail()
来获取声明值。
值得一提的是,任何检索声明值的方法只有在声明实际存在时才有效。即要求 ZoneInfo 声明将不起作用,除非该声明首先作为令牌请求的一部分被请求。