web-dev-qa-db-ja.com

.NET Core Web APIで現在のユーザーを取得するには(JWTトークンから)

多くの苦労(および多くのチュートリアル、ガイドなど)の後、保存されたユーザー名とパスワードが有効なときにJWTトークンを発行する認証コントローラーで小さな.NET Core REST Web APIをセットアップできました。

トークンは、サブIDとしてユーザーIDを保存します。

また、メソッドがAuthorizeアノテーションを使用するときにこれらのトークンを検証するようにWeb APIをセットアップすることもできました。

 app.UseJwtBearerAuthentication(...)

さて、私の質問:コントローラー(Web API)で(サブジェクトクレームに格納されている)ユーザーIDを読み取るにはどうすればよいですか?

基本的にはこの質問です( ASP .NET Coreで現在のユーザーを取得する方法 )が、Web APIの回答が必要です。そして、私はUserManagerを持っていません。だから私はどこかから主題の主張を読む必要があります。

27
monty

次の方法を使用できます。

var email = User.FindFirst("sub")?.Value;

私の場合、メールを一意の値として使用しています

17
Ateik

受け入れられた答えは私にはうまくいきませんでした。それが.NET Core 2.0の使用によるものなのか他の何かによるものなのかはわかりませんが、フレームワークがSubject ClaimをNameIdentifierクレームにマップしているようです。だから、次は私のために働いた:

string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

これは、Subject sub ClaimがJWTで設定され、その値がユーザーのIDであると想定していることに注意してください。

デフォルトでは、.NETのJWT認証ハンドラーは、JWTアクセストークンのサブクレームをSystem.Security.Claims.ClaimTypes.NameIdentifierクレームタイプにマップします。 [ソース]

GitHubのディスカッションスレッド もあり、この動作が混乱していると結論付けています。

33
Honza Kalfus

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;
10
ImpoUserC

多くの人がこの質問を見ているようですので、しばらく前に質問してから学んだ情報をもっと共有したいと思います。 (少なくとも私にとって)いくつかのことがより明確になり、それほど明白ではありませんでした(.NET初心者として)。

コメントで言及されたMarcusHöglund

「web api」についても同じである必要があります。ASP.NETCore 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を介してアクセスできます。

重要な副次的な注意点は、すべてのチュートリアルを見逃したことです。

.NETでJWTトークンを使用して何かを開始する場合は、 ClaimsIdentityClaimsPrinciple および Claim.Properties でも自信を持つことを忘れないでください。それがすべてです。今、あなたはそれを知っています。コメントの1つでHeringerによって指摘されました。


ALLクレームベース認証ミドルウェアは(正しく実装されている場合)認証中に受け取ったクレームをHttpContext.Userに設定します。

現在理解している限り、これはHttpContext.Userの値を安全に信頼できることを意味します。 しかし、waitミドルウェアを選択する際に気にすることを少し知っておいてください。 (.UseJwtAuthentication()に加えて)さまざまな認証ミドルウェアがすでに利用可能です。

小さなカスタム拡張メソッドを使用すると、現在のユーザーID(より正確な件名の主張)を取得できます。

 public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value; }

または、Ateikの回答でバージョンを使用します。


BUT WAIT:奇妙なことが1つあります

次に私を混乱させたのは、OpenID Connectの仕様によると、「サブ」クレーム(現在のユーザー)を探していましたが、見つかりませんでした。 LikeHonza Kalfusは彼の答えではできませんでした。

どうして?

なぜならマイクロソフトは「時々」「少し」異なるからです。または、少なくとももう少し(そして予期しない)ことをします。たとえば、元の質問で言及された公式のMicrosoft JWT Bearer認証ミドルウェア。マイクロソフトは、すべての公式認証ミドルウェアでクレーム(クレームの名前)を変換することにしました(互換性の理由から、詳細はわかりません)。

「サブ」クレームは見つかりません(ただし、OpenID Connectで指定された単一のクレームです)。 これらの派手なClaimTypes に変換されたためです。すべてが悪いわけではありません。異なるクレームを一意の内部名にマッピングする必要がある場合、マッピングを追加できます。

Microsoftの命名に固執するか(Microsoft以外のミドルウェアを追加/使用する場合は注意する必要があります)、またはMicrosoftミドルウェアのクレームマッピングを有効にする方法を見つけます。

JwtBearerAuthenticationの場合、それは行われます(StartUpの早い段階で、または少なくともミドルウェアを追加する前に行います)。

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

件名をMicrosoftの命名に固執したい場合(私に負けないで、Nameが正しいマッピングであるかどうかは今のところわかりません):

    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 Authenticationでは各リクエストのトークンを保存できます。ただし、これは(StartUp.ConfigureServices(...で)有効にする必要があります。

services
  .AddAuthentication("Bearer")
  .AddJwtBearer("Bearer", options => options.SaveToken = true);

文字列(またはnull)としての実際のトークン(すべて暗号形式)は、次の方法でアクセスできます。

HttpContext.GetTokenAsync("Bearer", "access_token")

このメソッドの古いバージョンがありました(これは、.NET Core 2.2で非推奨の警告なしで機能します)。

この文字列から値を解析して抽出する必要がある場合は、質問 JWTトークンのデコード方法 が役立つ場合があります。


まあ、その要約があなたにも役立つことを願っています。

5
monty

を使用してこれを行うことができます。

User.Identity.Name

2

HttpContextを使用しましたが、うまく機能します。

var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
    email = identity.FindFirst(ClaimTypes.Name).Value;
}
1
Wariored