オリジナル住所ます。https://www.blinkingcaret.com/2018/05/30/refresh-tokens-in-asp-net-core-web-api/
まず状態、私の英語の料理も、良い英語は自分で、次のテキストを参照、このブログのように単に翻訳、あまりにも痛い、それぞれの対面が見る見るために翻訳ソフトウェアを使用する必要があります
ウェブAPIを保護するためのアクセストークンを使用する場合は、最初に考えたのは、トークンの有効期限が切れたときに行う方法ですか?
あなたは再び資格情報をユーザーに尋ねましたか。これは良いオプションではありません。
このブログ記事では、この問題を解決するためにリフレッシュトークンを使用してについてです。特にで使用するASP.NETコアのWebアピスでJWTトークン。
まず第一に、本当に大きなものの右?あるのはなぜアクセストークンに直接それをより長く有効期限を設定しませんか?例えば、でも、月または年?
私たちがしなければ、誰かがそのトークンを取得するために管理しているので、彼らは、月、または年を使用することができます。パスワードを変更した場合でも。
トークンの署名が有効である場合、サーバはそれを信頼し、誰もが他の人のトークンになりますキー署名は、無効であるためにそれを変更する唯一の方法が有効でないためです。
それは選出されませんでした。これは、リフレッシュトークンを使用してのアイデアにつながります。
その後、トークンは、それを操作する方法でリフレッシュ?
リフレッシュトークン:あなたはアクセストークンを取得するとき、あなたは別のトークン1回の使用を取得します、想像してみてください。アプリケーションストアのリフレッシュトークンは、それを残します。
サーバはあなたが誰であるか知っているように、アプリケーションがサーバにリクエストを送信するたびに、アクセストークン(ベアラトークンここでは、許可)を送信します。トークンの有効期限が切れたある日には、サーバが何らかの方法で通知します。
これが起こるとき、あなたのアプリケーションが期限切れのトークンを送信し、トークンリフレッシュし、新しいトークンを取得し、トークンリフレッシュされます。だから、置き換え繰り返します。
疑わしいが起こるがある場合は、リフレッシュトークンは、アプリケーションが新しいアクセストークンを取得するためにそれを使用しようとすると、要求が拒否され、ユーザーは再度ログインする資格情報を入力する必要がありますことを意味し、取り消すことができます。
リフレッシュトークンストレージ要求(例えば、ダブリン、アイルランド)を作成するときにアプリケーションを想定して、最後の点の位置を明確にするために。ユーザーが情報にアクセスできる場合は、いくつかのログオンユーザーが場所を認識していないがある場合は、アクセストークンはアプリケーションを使う人満了時には使用し続けないように、ユーザーは、トークンリフレッシュを取り消すことができます。短命であることを良い考えかもしれない理由は(私がアクセストークンを持っている。ある数分間有効です)。
リフレッシュトークンを使用するために、我々は行うことができるようにする必要があります。
- アクセストークンを作成します(ここでは使用されますJWT)
- 作成、保存、およびトークン元に戻す(サーバー)を取得
- JWTは、トークンを期限切れにし、新しいトークンに交換トークンをリフレッシュし、トークンJWT(JWTすなわちリフレッシュトークン)更新されます
- 使用ASP。ユーザーを認証するために使用JWTトークンのNET認証ミドルウェア
- トークンの有効期限が切れているアプリケーションへのアクセス(オプション)を通知する方法があります
- トークンの有効期限が切れた場合、クライアントは透過的に新しいトークンを取得することができます
あなたはこれらのトピックの情報を分離する必要がある場合は、続けてください。あなたが一緒にこれらの作業のすべてを見たい場合は、デモプロジェクト(ここに見つけることができ、ここでのデモプロジェクトの壁紙を)。
JWTは、アクセストークンを作成します
あなたはASP.NETコアでJWTを使用する方法の詳細な説明をしたい場合、私はチェックアウトをお勧めしますASP.NETコア内のWeb APIを固定します。これは要約です。。
まず、我々は、追加する必要があり System.IdentityModel.Tokens.Jwt
、パッケージを:
$ dotnet add package System.IdentityModel.Tokens.Jwt
新しいトークンJWTを作成します。
プライベート文字列GenerateToken(IEnumerableを<項>の特許請求の範囲) { VARキー=新しいSymmetricSecurityKey(Encoding.UTF8.GetBytes(「JWTトークンに署名するために使用されるサーバ鍵はここで、16個の以上の文字を使用します」))。 VaRのJWT =新しいJwtSecurityToken(発行者: "Blinkingcaret"、 観客: "誰もが"、 主張:たとえば新しいクレーム[] {新しいクレーム(ClaimTypes.Name、 "ユーザ名")の請求、//ユーザーの主張を、// ... notBeforeの:DateTime.UtcNowは、 有効期限が切れる:DateTime.UtcNow.AddMinutes(5)、 signingCredentials:新しいSigningCredentials(キー、SecurityAlgorithms.HmacSha256) ); 返す新しいJwtSecurityTokenHandler()WriteToken(JWT)。 }
ここでは、新しいJWTトークンを作成し、その有効期限は、HMACSHA256記号を使用して、5分です。
作成、保存、およびトークンの取り消しを取得
リフレッシュタグは一意である必要があり、それはそれらを推測することは不可能(もしくは難しい)があります。
シンプルなGUIDは、この条件を満たすように表示されます。残念ながら、GUID生成プロセスがランダムではありません。これは、いくつかのGUIDを与え、あなたは簡単に次のGUIDを推測することができることを意味します。
ありがたいことに、ASPで安全な乱数ジェネレータがあります。NETコアは、我々はそれらのいくつかが与えられている場合でも、一意の文字列を生成するために使用することができ、のような次の外観を予測することは困難です。
System.Security.Cryptographyを使用しました。 // ... 公衆ストリングGenerateRefreshToken() { VARの乱数=新しいバイト[32]。 使用(VAR RNG = RandomNumberGenerator.Create()){ rng.GetBytes(乱数)。 Convert.ToBase64String(乱数)を返します。 } }
ここでは、32バイトの乱数を生成し、我々はそれを文字列として使用できるように、BASE64に変換します。それはユニークで困難を生じるはずであることを除いて、長さにはガイダンスは、トークンよりも推測することがありません。私は32を選びましたが、16も利用可能です。
私たちはあなたの最初のトークンは、JWTを生成し、期限切れのトークンを「更新」されたときにリフレッシュトークンを生成する必要があります。
たびに新しいリフレッシュトークン生成を保存し、我々はユーザーの問題へのリンクアクセストークンのようにする必要があります。
最も簡単な方法は、追加の列を追加し、ユーザーテーブルリフレッシュマークのためです。その結果、ユーザーだけが一箇所で(各ユーザーだけつの有効なリフレッシュトークン)でログインすることができます。
また、各ユーザーに対して複数のリフレッシュトークンを維持し、ユーザーのための活動報告を提供するために、彼らの要求を開始するから、場所、時間を節約することができます。
また、中に、例えば、数日後に(有効期限は、リフレッシュトークンと一緒に保存する必要があります)良い考えかもしれませんリフレッシュトークンの有効期限が切れてみましょう。
一つは、リフレッシュ動作で使用する場合、それは複数回使用できないようにリフレッシュタグは、それを削除することを忘れてはなりません。
JWTは失効し、新しいトークンに交換トークンをリフレッシュし、トークンJWT(JWTすなわちリフレッシュトークン)更新されます
新しいアクセストークンを取得するために、期限切れのアクセストークンから、我々はトークンの有効期限が切れた場合でも、トークンに声明にアクセスできるようにする必要があります。
JWTの認証を使用しているときに、ユーザー認証のためのASP.NETコアミドルウェアを使用すると、期限切れのトークンに応じて、401を返します。
私たちは、匿名ユーザーは、コントローラのアクションを作成することができ、かつJWTとリフレッシュトークンに同意する必要があります。
私たちは、匿名ユーザーは、コントローラのアクションを作成することができ、かつJWTとリフレッシュトークンに同意する必要があります。
コントローラのアクションでは、我々は(あなたがトークン寿命を無視することを選択できます)、およびそこに含まれるユーザーに関するすべての情報を抽出し、手動で期限切れのアクセストークンを検証する必要があります。
その後、我々は、リフレッシュトークンに格納されている情報を取得するために、ユーザーを使用することができます。その後、私たちは、保存されたトークンは、要求で送信されたトークンと比較されてリフレッシュすることができます。
すべてがうまくいけば、我々は、新しいJWTとリフレッシュトークンを作成する新しいリフレッシュトークンを保存し、古いものを捨てて、クライアントに新しいJWTとリフレッシュトークンを送信します。
ここでは、期限切れのトークンJWTのユーザ情報からClaimsPrincipalフォームを取得する方法を次のとおりです。
プライベートClaimsPrincipal GetPrincipalFromExpiredToken(文字列トークン) { VARのtokenValidationParametersは=新しいTokenValidationParameters { あなたのユースケースに応じて、観客と発行者を検証することをお勧めしますValidateAudience = falseを、// ValidateIssuer = falseを、 ValidateIssuerSigningKey =真、 IssuerSigningKey =新しいSymmetricSecurityKey(Encoding.UTF8 .GetBytesは(「JWTトークンに署名するために使用されるサーバキーは、ここにある16個の以上の文字を使用する」))、 ValidateLifetimeは= falseを//ここで我々は、トークンの有効期限を気にしないことを言っています }; VARトークンハンドラ=新しいJwtSecurityTokenHandler(); セキュリティトークンセキュリティトークン。 VaRの主要= tokenHandler.ValidateToken(トークン、tokenValidationParameters、セキュリティトークンアウト)。 JwtSecurityTokenとしてVAR jwtSecurityToken =セキュリティトークン。 (もし!jwtSecurityToken == nullの|| jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256、StringComparison.InvariantCultureIgnoreCase)) 新しいSecurityTokenExceptionを投げる( "無効なトークンを"); 元本を返します。 }
上記のコードは、注目すべき部分である、我々は、このように有効期限が切れたトークンが有効であると考えられる、TokenValidationParametersに偽ValidateLifeTime =を用います。また、当社は、トークンが(この場合はHMACSHA256)我々の期待を満たしている署名に使用するアルゴリズムをチェックしています。
この理由は、理論的には、あなたはJWTトークンを作成することができ、ということで、署名アルゴリズムは、「なし」に設定されている(「なし」に署名アルゴリズムでSET。)。JWTトークンは(符号なしていても)有効です。有効なリフレッシュトークンをこのように使用し、それは本当のJWTトークンに置き換え偽トークンすることができます。
今、私たちは、コントローラを移動する必要があります(それが副作用と長いトークンクエリ文字列パラメータを持っているので、それは、ポストする必要があります):
[HttpPost] 公共IActionResultリフレッシュ(文字列トークン、列refreshToken) { VAR主体= GetPrincipalFromExpiredToken(トークン)。 VARのユーザー名= principal.Identity.Name。 VAR savedRefreshToken = GetRefreshToken(ユーザー名); //データストアから更新トークンを取得する (!savedRefreshToken = refreshToken)場合には 、新たなSecurityTokenException(「無効なリフレッシュトークン」)を投げます。 VAR newJwtToken = GenerateToken(principal.Claims)。 VAR newRefreshToken = GenerateRefreshToken()。 DeleteRefreshToken(ユーザー名、refreshToken)。 SaveRefreshToken(ユーザー名、newRefreshToken)。 戻り新しいたObjectResult(新しい{ トークン= newJwtToken、 refreshToken = newRefreshToken })。 }
この断片は、上記の仮定を含みます。私は、取得、保存、および削除するために省略し、だけでなく、各ユーザーはトークンのみリフレッシュを持っている、これは最も単純なシナリオであることを前提としています。
asp.netコア認証ミドルウェアは、ユーザーを認証するためにJWTトークンを使用しています
我々は、有効で頭の要請場合ように、asp.netコアミドルウェア・パイプラインを設定する必要があります Authorization: Bearer JWT_TOKEN
(認証は、ユーザーが「ログイン」さ「に署名します」)
あなたが特にasp.netコアにJWTを設定する方法についてのより詳細な議論をしたい場合は、チェックASP.NETコア内のWeb APIをセキュア ..
ASP.NET Core2.0バージョンの後、我々は認証ミドルウェアでパイプラインに追加し、中configureservicesのstartup.csを設定します。
公共のボイドConfigureServices(IServiceCollectionサービス) { services.AddMvc(); // ... services.AddAuthentication(オプション=> { options.DefaultAuthenticateScheme = "ベアラー"; options.DefaultChallengeScheme = "ベアラー"; })。AddJwtBearer( "ベアラ"、オプション=> { options.TokenValidationParameters =新しいTokenValidationParameters { ValidateAudience =偽、 ValidateIssuer = falseを、 ValidateIssuerSigningKey =真、 IssuerSigningKey =新しいSymmetricSecurityKey(Encoding.UTF8.GetBytes(「JWTトークンに署名するために使用されるサーバキーはここにあるが、16個の以上の文字を使用する」))、 ValidateLifetime = trueの場合、 この設定のクロックス= TimeSpan.Zero //デフォルトであります5分 }。 options.Events =新しいJwtBearerEvents { OnAuthenticationFailed =コンテキスト=> { IF(context.Exception.GetType()== typeof演算(SecurityTokenExpiredException)) { context.Response.Headers.Add( "トークン期限切れ"、 "真")。 } Task.CompletedTaskを返します。 } }。 }); }
上記のコードは、イベントの処理をonAuthenticationFailedされることに注意してください。リクエストトークンの有効期限が切れた場合は、応答が先頭に追加されたトークンを期限切れになります。クライアントは、リフレッシュトークンを使用することを決定するために、この情報を使用することができます。しかし、私たちは、クライアントが、それは401応答を受信したときにリフレッシュトークンを使用しようとすることを可能にすることができます。我々は有効期限が切れたトークン回復ブログの頭に依存しますポスト要求(オリジナル訳:私たちは、トークンに依存しますが、このブログで記事の残りの部分に応じて、頭を満了します)。
今、私たちはただのパイプラインの認証ミドルウェアに追加する必要があります。
公共ボイド設定(IApplicationBuilderアプリ、IHostingEnvironmentのENV) { IF(env.IsDevelopment()) { app.UseDeveloperExceptionPage()。 } app.UseAuthentication()。 // ...
クライアント
ここでの目標は、クライアントがそのトークンの有効期限が切れる実現することができ、そして新しいトークンを取得するために適切な行動を取る際に、APIクライアントを構築することで、透過的に、これらすべての操作を実行します。
アクセストークンの有効期限が切れた場合、要求がトークンにアクセスし、リフレッシュするために使用されなければならないので、失敗したエンドポイントをリフレッシュするための新しい要求を送信します。新しいトークンを取得するための要求と、クライアントが終了した後、元の要求を繰り返すべきです。
これが達成されるであろう、あなたが使用しているクライアントの種類によって異なります。ここでは、可能なjavascriptのクライアントを記述します。私たちは、要求に対する応答に依存し、リクエストトークンは、「トークン有効期限が切れた」という名前の頭に有効期限が切れています。我々は、Web APIのための要求を実行するためにフェッチを使用します。
非同期関数fetchWithCredentials(URL、オプション){ VAR jwtToken = getJwtToken()。 オプション=オプション|| {}; options.headers = options.headers || {}; options.headers [ '許可'] = 'ベアラ' + jwtToken。 VAR応答=(URL、オプション)をフェッチ待ちます。 (response.ok){//すべては良いですが、応答を返す場合 、戻り応答を、 } IF(のResponse.Statusの=== 401 && response.headers.has( 'トークン期限切れ')){ VAR refreshToken = getRefreshToken()。 VaRのrefreshResponse =は(jwtToken、refreshToken)をリフレッシュ待ちます。 (!refreshResponse.ok){かの 応答を返します。//そう、元401応答を返すリフレッシュすることができませんでした } VARのjsonRefreshResponseは= refreshResponse.json()を待ちます。//新しいトークンでJSONを読む saveJwtToken(jsonRefreshResponse.token)。 saveRefreshToken(jsonRefreshResponse.refreshToken)。 リターンはfetchWithCredentials(URL、オプション)を待ちます。//元の要求繰り返し // {}他のステータスが401ではない、および/またはいかなるトークン期限切れヘッダがない リターン応答を、//元の401応答を返します } }
そこgetjwttoken、getrefreshtoken、savejwttokenと上記のコードの断片でsaverefreshttoken。保存し、トークンを取得するために、ローカルに保存されたブラウザを使用して、ブラウザでは、
例えば:
機能getJwtToken(){ localStorage.getItem( 'トークン')を返します。 } 関数getRefreshToken(){ localStorage.getItem( 'refreshToken')を返します。 } 関数saveJwtToken(トークン){ localStorage.setItem( 'トークン'トークン)。 } 関数saveRefreshToken(refreshToken){ localStorage.setItem( 'refreshToken'、refreshToken)。 }
機能はありリフレッシュ。このAPI関数は、例えば、エンドポイントが配置されている場合/トークン/リフレッシュトークンを更新するエンドポイントPOST要求を行います。
非同期関数リフレッシュ(jwtToken、refreshToken){ リターン( 'トークン/リフレッシュ'フェッチ、{ 方法: 'POST'、 身体: `トークン= $ {encodeURIComponentで(jwtToken)}&refreshToken = $ {encodeURIComponentで(getRefreshToken())}` ヘッダー:{ 'のContent-Type': 'アプリケーション/ x-www-form-urlencodedで' } })。 }
あなたはクローム開発ツールのコンソールで、このクライアントを使用しようとすると、それは次のようになります。
ここで注意すべきことの一つは、401コンソールが赤色で表示されています。要求がトークンに起因する障害が発生した場合、この問題が発生した期限が切れます。
あなたは(それが有効なステータスコードは、この場合は適切であるため、引用符でエラー)、「間違った」を表示しないようにしたい場合は、JavaScriptでトークン有効期限にアクセスし、満期日前にすることができますそれを更新します。
JWTトークンは、三つの部分を持っている「」1で区切られています。第二部は、ユーザーの宣言、トークンが有効期限のUNIXタイムスタンプを含み、EXP呼び出したステートメントが含まれています。これがどのようにのjavascriptの日付オブジェクトJWTトークンの有効期限と取得方法:
VAR請求= JSON.parse(ATOB(token.split()[1]) ''); VaRのEXPIRATIONDATE =新しいDate(claims.exp * 1000); // UNIXタイムスタンプは、秒単位でミリ秒単位のjavascriptのです
非常に複雑な気分有効期限を確認し、そう(処理ゾーンが問題になる可能性がある一方で)私はそれをお勧めしません。私はそれは、トークンを無効にしない限り、改ざんすることはできません、それは非常に明確にあなたがJWTトークン事に入れることを私たちになり非常に興味深いものが、秘密ではないされているので、これは言及することを決めました。
私はあなたがこの記事が面白いと願っています。このような場合は、コメントでラインを入れました。