Ответ 1
Вы можете использовать этот метод:
var email = User.FindFirst("sub")?.Value;
В моем случае я использую электронное письмо как уникальное значение
После множества проблем (и много тутуриалов, руководств и т.д.) мне удалось настроить небольшой .NET API REST Web-сайта с Auth Controller, выдающим токены JWT, когда сохранены имя пользователя и пароль.
Токен хранит идентификатор пользователя как подпункт.
Мне также удалось настроить веб-API для проверки этих токенов, когда метод использует аннотацию Authorize.
app.UseJwtBearerAuthentication(...)
Теперь мой вопрос: Как я могу прочитать идентификатор пользователя (сохраненный в заявке) в моих контроллерах (в веб-API)?
В основном этот вопрос (Как получить текущего пользователя в ASP.NET Core), но мне нужен ответ для веб-api. И у меня нет UserManager. Поэтому мне нужно где-то прочитать предметный иск.
Вы можете использовать этот метод:
var email = User.FindFirst("sub")?.Value;
В моем случае я использую электронное письмо как уникальное значение
Принятый ответ не работал для меня. Я не уверен, было ли это вызвано тем, что я использовал .NET Core 2.0 или чем-то еще, но похоже, что фреймворк сопоставляет требование субъекта с утверждением NameIdentifier. Итак, у меня сработало следующее:
string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
Обратите внимание, что это предполагает, что Subject sub
Claim установлен в JWT, а его значением является идентификатор пользователя.
По умолчанию обработчик аутентификации JWT в .NET сопоставляет System.Security.Claims.ClaimTypes.NameIdentifier
заявку токена доступа JWT с типом заявки System.Security.Claims.ClaimTypes.NameIdentifier
. [Источник]
На GitHub также есть дискуссионная ветка, в которой они приходят к выводу, что такое поведение сбивает с толку.
Если вы используете 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)
};
В каждом методе контроллера вы можете скачать идентификатор текущего пользователя:
var claimsIdentity = this.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.Name)?.Value;
Похоже, что многие люди смотрят на этот вопрос, поэтому я хотел бы поделиться некоторой дополнительной информацией, которую я узнал, так как я задал вопрос некоторое время назад. Это делает некоторые вещи более понятными (по крайней мере, для меня) и не было так очевидно (для меня, как новичок .NET).
Как отметил в комментариях Маркус Хеглунд:
Это должно быть то же самое для "web api". В ASP.NET Core Mvc и Web Api объединены для использования одного контроллера.
Это определенно верно и абсолютно правильно.
Потому что это все одинаково для .NET и .NET Core.
Назад, чем я был новичком в .NET Core и на самом деле весь мир .NET. Важной недостающей информацией было то, что в .NET и .NET Core вся аутентификация может быть обрезана до пространства имен System.Security.Claims с его ClaimsIdentity, ClaimsPrinciple и Claims.Properties. И поэтому он используется в обоих типах контроллеров .NET Core (API и MVC или Razor или...) и доступен через HttpContext.User
.
Важное примечание: все учебники пропущены.
Поэтому, если вы начнете что-то делать с токенами JWT в .NET, не забудьте также убедиться в надежности ClaimsIdentity, ClaimsPrinciple и Claim.Properties. Это все об этом. Теперь ты это знаешь. Это было указано Герингером в одном из комментариев.
ВСЕ промежуточные программы аутентификации на основе утверждений (если они правильно реализованы) заполняют HttpContext.User
утверждениями, полученными во время проверки подлинности.
Насколько я понимаю, теперь это означает, что можно безопасно доверять значениям в HttpContext.User
. Но подождите немного, чтобы понять, что нужно учитывать при выборе промежуточного программного обеспечения. Уже доступно много различных промежуточных программ аутентификации (в дополнение к .UseJwtAuthentication()
).
С небольшими настраиваемыми методами расширения вы можете теперь получить текущий идентификатор пользователя (точнее утверждение субъекта)
public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value; }
Или вы используете версию в ответ Ateik.
НО ЖДУ: есть одна странная вещь
Следующее, что меня смутило, это то, что: в соответствии со спецификацией OpenID Connect я искал "суб" заявку (текущий пользователь), но не смог ее найти. Как Хонза Кальфус не мог сделать в своем ответе.
Зачем?
Потому что Microsoft "иногда" "немного" отличается. Или, по крайней мере, они делают немного больше (и неожиданных) вещей. Например, официальное промежуточное программное обеспечение аутентификации Microsoft JWT Bearer, упомянутое в первоначальном вопросе. Microsoft решила преобразовать претензии (названия претензий) во все их официальное промежуточное ПО аутентификации (по причинам совместимости я не знаю более подробно).
Вы не найдете "подчиненную" заявку (хотя это единственная заявка, указанная OpenID Connect). Потому что он превратился в эти модные ClaimTypes. Это не все плохо, это позволяет вам добавлять сопоставления, если вам нужно сопоставить разные заявки с уникальным внутренним именем.
Либо вы придерживаетесь именования Microsoft (и должны помнить об этом, когда добавляете/используете промежуточное программное обеспечение не от Microsoft), либо вы узнаете, как изменить соответствие утверждений для промежуточного программного обеспечения Microsoft.
В случае JwtBearerAuthentication это сделано (сделайте это рано в StartUp или по крайней мере перед добавлением промежуточного программного обеспечения):
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 позволяет сохранять токен для каждого запроса. Но это нужно активировать (в StartUp.ConfigureServices(...
).
services
.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => options.SaveToken = true);
Фактический токен (во всей его загадочной форме) в виде строки (или ноль) может быть доступен через
HttpContext.GetTokenAsync("Bearer", "access_token")
Была более старая версия этого метода (это работает для меня в .NET Core 2.2 без устаревшего предупреждения).
Если вам нужно разобрать и извлечь значения из этой строки, может помочь вопрос, как декодировать токен JWT.
Ну, я надеюсь, что это резюме поможет вам.
Вы можете сделать это, используя.
User.Identity.Name
Я использовал HttpContext, и он хорошо работает:
var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
email = identity.FindFirst(ClaimTypes.Name).Value;
}