在经历了很多挣扎之后(以及很多略含型,指南等),我设法设置了一个小的.NET核心REST Web API,并在存储用户名和用户名时会发出JWT代币的auth Controller密码有效。
令牌将用户ID存储为sub索赔。
我还设法设置了Web API以验证这些令牌时,当一种方法使用授权注释时。
app.usejwtbearerauthentication(...)
现在我的问题:如何在控制器(Web API中)读取用户ID(存储在主题声明中)?
基本上是这个问题(如何在ASP .NET Core中获取当前用户),但是我需要Web API的答案。而且我没有Usermanager。因此,我需要从某个地方阅读主题主张。
接受的答案对我不起作用。我不确定这是我使用.NET Core 2.0或其他其他内容引起的,但是看起来该框架将主题声称映射到名称识别符声明。因此,以下对我有用:
string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
请注意,这是假设主题sub
声明是在JWT中设置的,其值是用户的ID。
默认情况下,.NET中的JWT身份验证处理程序将将JWT访问令牌的子声明映射到System.Security.Claims.ClaimTypes.NameIdentifier
索赔类型。[来源]
github上还有一个讨论线程,他们结论这种行为令人困惑。
您可以使用此方法:
var email = User.FindFirst("sub")?.Value;
就我而言,我将电子邮件用作唯一值
似乎很多人都在看这个问题,所以我想分享一些我在一段时间问这个问题以来就学到的更多信息。这使一些事情变得更加清楚(至少对我来说)并不那么明显(对我来说是.net newbie)。
AS MarcusHöglund评论中提到的:
对于" Web API"。在ASP.NET Core MVC和Web API中合并以使用相同的控制器。
绝对是正确的,绝对正确的。
因为在.NET和.NET核心上都是相同的。
Back比我是新手核心的新手,实际上是完整的.NET世界。重要的缺失信息是,在.NET和.NET核心中,所有身份验证都可以将其修剪到系统。Security.Security.claims命名空间及其索赔,索赔要求和索赔。因此,它都用于两种.NET核心控制器类型(API和MVC或Razor或...)中,并且可以通过HttpContext.User
访问。
一个重要的旁注
因此,如果您开始使用.NET中的JWT令牌做某事,请不要忘记也对索赔的索赔,索赔要求和索赔。就是这样。现在你知道了。它由 Heringer 在其中一个评论中指出。
所有基于索赔的身份验证中间Wares(如果正确实施)将填充HttpContext.User
,并在身份验证期间收到的索赔。
据我了解,这意味着人们可以安全地信任HttpContext.User
中的值。,但是请等待在选择中间件时知道什么。有很多不同的身份验证中间件已经可用(除了.UseJwtAuthentication()
之外)。
使用小型自定义扩展方法,您现在可以获取当前的用户ID(更准确的主题声明),例如
public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value; }
或您在 ateik 的答案中使用版本。
但是等待:有一件奇怪的东西
下一件事使我更加困惑,而不是:根据OpenID Connect Spec,我正在寻找" Sub"声明(当前用户),但找不到它。像 Honza Kalfus 无法在他的回答中做。
为什么?
因为微软是"有时"的"有点"不同的。或者至少他们会做更多(和意外)的事情。例如,原始问题中提到的官方Microsoft JWT携带者身份验证中间件。微软决定在其所有官方身份验证中间件中转换索赔(索赔的名称)(出于兼容的原因,我不知道)。
您找不到" sub"索赔(尽管这是OpenID Connect指定的单一声明)。因为它被转换为这些花哨的索赔类型。这还不是不错,如果需要将不同的索赔映射到唯一的内部名称中,它允许您添加映射。
您要么坚持使用Microsoft命名(并且要介意添加/使用非Microsoft Mifdredware时),要么找出如何将Microsoft Middleware的索赔映射。
在JWTbeAreraUthentication的情况下,它已经完成(在启动早期或至少在添加中间件之前进行):
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
如果您想坚持Microsoft命名主题主张(请勿击败我,我不确定现在名称是正确的映射):
public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier, StringComparison.OrdinalIgnoreCase))?.Value; }
请注意,其他答案使用更高级和更方便的Findfirst方法。尽管我的代码示例显示了它,但您可能应该随身携带。
因此,您的所有索赔都可以在HttpContext.User
中存储和访问(通过一个名称或另一个名称)。
但是我的令牌在哪里?
我不知道其他中间件,但是JWT Bearer身份验证允许为每个请求保存令牌。但这需要激活(在StartUp.ConfigureServices(...
中)。
services
.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => options.SaveToken = true);
然后可以通过
访问字符串(或null)的实际令牌(在所有的神秘形式中)HttpContext.GetTokenAsync("Bearer", "access_token")
有这种方法的较旧版本(这在.net core 2.2中对我有用,而无需弃用警告)。
如果您需要从该字符串中解析和提取值,则可能会如何解码JWT Token帮助。
好吧,我希望摘要也对您有所帮助。
如果您使用Name
将ID
存储在此处:
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
在每个控制器方法中,您可以通过以下方式获取当前用户的ID
var claimsIdentity = this.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.Name)?.Value;
我使用了httpcontext,它运行良好:
var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
email = identity.FindFirst(ClaimTypes.Name).Value;
}
在我的情况下,我将sipertypes.name设置为唯一用户电子邮件,然后在jwt token生成之前:
claims.Add(new Claim(ClaimTypes.Name, user.UserName));
然后,我将唯一的用户ID存储到sipertypes.nameidentifier:
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
然后在控制器的代码中:
int GetLoggedUserId()
{
if (!User.Identity.IsAuthenticated)
throw new AuthenticationException();
string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
return int.Parse(userId);
}
您可以使用。
user.Identity.name
我使用以下代码在.NET Core 5 Web API
中使用以下代码User.Claims.First(x => x.Type == "id").Value;
asp.net核心身份获取用户ID
public async Task<IActionResult> YourMethodName()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier) // will give the user's userId
var userName = User.FindFirstValue(ClaimTypes.Name) // will give the user's userName
ApplicationUser applicationUser = await _userManager.GetUserAsync(User);
string userEmail = applicationUser?.Email; // will give the user's Email
}
.net核心身份获取用户ID
public static class ClaimsPrincipalExtensions
{
public static T GetLoggedInUserId<T>(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(T) == typeof(string))
{
return (T)Convert.ChangeType(loggedInUserId, typeof(T));
}
else if (typeof(T) == typeof(int) || typeof(T) == typeof(long))
{
return loggedInUserId != null ? (T)Convert.ChangeType(loggedInUserId, typeof(T)) : (T)Convert.ChangeType(0, typeof(T));
}
else
{
throw new Exception("Invalid type provided");
}
}
public static string GetLoggedInUserName(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Name);
}
public static string GetLoggedInUserEmail(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Email);
}
}