JWTとは何ですか?この記事では、JWT について理解し、ノードでの JWT のアプリケーションを紹介し、JWT のメリットとデメリットについて説明します。
JWT はJSON Web Tokenの略称で、従来の認証メカニズムでは次の手順にすぎません。
1. ユーザーはアカウントのパスワードをサーバーに送信します。 2. サーバーはアカウントのパスワードを検証した後、ユーザー関連の情報、ユーザーの役割、有効期限などを現在のセッションに保存します。 3. サーバーはユーザーに session_id を与え、それをユーザーの Cookie に書き込むか、クライアントがそれをローカルに保存します。 4. ユーザーがサービスをリクエストするたびに、Cookie またはその他の方法でこの session_id を取得する必要があります。 5. サーバーはそれを受信すると、データベースに戻って現在の session_id を照会し、ユーザーに権限があるかどうかを確認します。
このモデルの利点は、サーバーがいつでもユーザーの権限を終了でき、データベースにアクセスして現在のユーザーのセッション情報を変更または削除できることです。ただし、欠点もあります。つまり、サーバー クラスターの場合、各サーバーが同じセッション ストレージ情報を取得できるようにするために、すべてのマシンがセッション情報を共有する必要があります。これらの問題は解決できますが、作業量は膨大です。
JWT ソリューションの利点は、この情報が保存されないことです。要求が受け入れられるたびに、トークン データが検証されるだけで済みます。
JWT の原理について簡単に説明します。実際、クライアントが認証リクエストを送信すると、サーバーはユーザーを認証した後に JSON オブジェクトを生成します。これには、おそらく「あなたは誰ですか、何をしているのかなど」のような情報が含まれます。 .,有効期限" 、重要なことは、有効期限が存在する必要があるということです。一般的な形式は次のとおりです。
{ ユーザー名: "泥棒ストリングer", 役名:「ワールドコードファーマー」、 endTime: "2022 年 5 月 20 日" }
ただし、そのような表面的な方法で送信されることはありません。指定された署名アルゴリズムと送信されたペイロードに関する情報に基づいて、可逆署名アルゴリズムを通じて署名および送信されます。一般的な形式を表すために画像を使用します。 :
図からわかるように、返される情報は大きく 3 つの部分に分かれており、左側は署名後の結果であり、右側はデコードされたソース コードです。赤、紫、シアンの 3 色はそれぞれ「ドット」で区切られています。
最初の赤い部分はヘッダーです。ヘッダーは主にメソッドを指定します。図の署名アルゴリズム (デフォルトは HS256 ) は、2 つの当事者間で 1 つの鍵のみを共有します。フィールド識別子は JWT タイプです。
2 番目の紫色の部分であるペイロードは、送信される実際のデータである JSON オブジェクトです。使用できる公式フィールドは 7 つあります。
iss (発行者): 発行者
exp (有効期限): 有効期限
sub(件名):件名
aud (聴衆): 聴衆
nbf (Not Before): 有効時間
iat (発行時刻): 発行時刻
jti (JWT ID): 番号
これらのフィールドに加えて、いくつかのカスタム フィールドを作成することもできます。JWT はデフォルトでは暗号化されないため、使用する際は機密データを使用しないように注意してください。
3 番目の部分はSignature
署名です。この部分は、ユーザーが指定したサーバー上にのみ存在する秘密鍵であり、ヘッダーで指定されたアルゴリズムを使用して、次の署名方法で署名されます。
以下では具体的な使い方を体験してみましょう。
ステップ 1: nodejs プロジェクトを構築する必要があります。 npm init -y
使用してプロジェクトを初期化し、 express
、 jsonwebtoken
、 nodemon
などの依存関係をインストールする必要があります。
$ npm 私は jsonwebtoken ノードモンを表現します
次に、 package.json
のscripts
フィールドにnodemon app.js
コマンドを追加します。
「スクリプト」: { "start": "nodemon app.js" }、
ステップ 2: ノード アプリケーションを初期化し、ルート ディレクトリに app.js ファイルを作成します。
// app.js const Express = require("express"); const app = Express(); app.use(express.json()); app.listen(3000, () => { console.log(3000 + "listening...") // ポート 3000 をリッスンします});
ステップ 3: jsonwebtoken
依存関係を導入し、インターフェイスとサーバーの秘密キーを作成します。
// app.js //... const jwt = require("jsonwebtoken"); const jwtKey = "~!@#$%^&*()+,"; // ...
ここでのjwtKey
、サーバーにのみ保存されるカスタム秘密キーです。その後、ログイン用の /login インターフェイスの作成を開始し、検証用のローカル シミュレーション データベースを作成し、 jwt.sign
メソッドを通じて実行しました。署名を検証します:
// app.js const データベース = { ユーザー名: "ユーザー名", パスワード: "パスワード", }; app.post("/login", (req, res) => { const { ユーザー名、パスワード } = req.body; if (ユーザー名 === データベース.ユーザー名 && パスワード === データベース.パスワード) { jwt.sign( { ユーザー名、 }、 jwtキー、 { 有効期限: "30S", }、 (_, トークン) => { res.json({ ユーザー名、 メッセージ: "ログイン成功", トークン、 }); } ); } });
上記のコードでは、ログインを検証するためのローカル アカウントとパスワード データベースの作成をシミュレートするdatabase
変数を作成し、アカウントとパスワードが完全に一致することを確認した後、 jsonwebtoken
を/login
てpost
しました。このメソッドには、 jwt
3 つのsign
署名があります。
エクスポート関数の記号( ペイロード: 文字列バッファ | SecretOrPrivateKey: 秘密、 オプション?: SignOptions、 ): 弦; エクスポート関数の記号( ペイロード: 文字列バッファ | SecretOrPrivateKey: 秘密、 コールバック: SignCallback、 ): 空所; エクスポート関数の記号( ペイロード: 文字列バッファ | SecretOrPrivateKey: 秘密、 オプション: SignOptions、 コールバック: SignCallback、 ): 空所;
ここでは、関数オーバーロードの方法を使用して、最後のインターフェイス シグネチャを実装します。最初のパラメータは、カスタム オブジェクト タイプ、 Buffer
タイプ、または直接string
タイプにすることができます。ソース コードでは、 object
タイプを使用します。 JWT は署名時にこれらのデータにも署名するため、一部のフィールドをカスタマイズします。ただし、JWT はデフォルトでは暗号化されないため、ここでは機密データを使用しないように注意してください。これにより、データが暗号化されます。改ざんされておらず、署名をチェックするプロセスは検証 と呼ばれます。
もちろん、元のトークンを暗号化して送信することもできます。
2 番目のパラメータ: は、署名のためにサーバーに保存される秘密鍵です。通常、JWS は JWA によって提供される HS256 アルゴリズムと鍵を使用します。ただし、分散シナリオでは、この鍵に厳密に依存します。複数のサービスで JWT を検証する必要がある場合があります。キーが各サービスに保存されている場合、そのキーが漏洩すると、誰でも自由に JWT を偽造できることを知っておく必要があります。
3 番目のパラメーター: 署名オプションSignOptions
インターフェイスの署名) です。
エクスポートインターフェイス SignOptions { アルゴリズム?: アルゴリズムが未定義です。 キー ID?: 文字列が定義されていません。 期限切れ?: 文字列番号 | /** 秒または期間を表す文字列で表されます [zeit/ms](https://github.com/zeit/ms.js) 例: 60、「2 日」、「10h」、「7d」。 */ notBefore?: 文字列番号 | 未定義 対象者?: 文字列 | 文字列[] | 件名?: 文字列 | 未定義; 発行者?: 文字列 | 未定義; jwtid?: 文字列が定義されていません。 mutatePayload?: ブール値 | 未定義 noTimestamp?: ブール値 | 未定義。 ヘッダー?: JwtHeader | 未定義; エンコード?: 文字列 | 未定義; }
ここでは、 expiresIn
フィールドを使用してエージング時間を指定します。使用方法については、このドキュメントを参照してください。
4 番目のパラメーターはコールバックです。コールバックの 2 番目のパラメーターは、署名を通じて生成されたtoken
です。最終的に、このtoken
フロント エンドにローカルに保存され、検証のためにサーバーに送信されるようになります。あらゆるリクエストに応じて。
次に、このインターフェイスを検証してみましょう。vscode にREST クライアントプラグインをインストールし、ルート ディレクトリにrequest.http
ファイルを作成し、要求された情報をファイルに書き込みました。
POST http://localhost:3000/login コンテンツタイプ: アプリケーション/json { "ユーザー名": "ユーザー名", "パスワード": "パスワード" }
次に、コマンド ラインでnpm run start
コマンドを実行してサービスを開始し、 requset.http
ファイルの上にあるSend Request
ボタンをクリックしてリクエストを送信します。
リクエストが成功すると、次のような応答メッセージが表示されます。
token
フィールドは、JWT によって生成されたtoken
です。
このtoken
が有効かどうかを確認してみましょう。ログイン後のインターフェイスを作成しています。
app.get("/afterlogin", (req, res) => { const {ヘッダー} = 要求; const トークン = headers["authorization"].split(" ")[1]; //ヘッダーの認証フィールドにトークンを入れます jwt.verify(token, jwtKey, (err, payload) => { if (err) は res.sendStatus(403) を返します。 res.json({ メッセージ: "認証が成功しました", ペイロード }); }); });
このコードでは、リクエスト ヘッダーのauthorization
フィールドのtoken
を取得することで、JWT を通じて以前に生成されたtoken
取得します。 次に、 jwt.verify
検証メソッドを呼び出して、 token
が有効かどうかを検証します。このメソッドには 3 つのパラメータがあります。
// インターフェースの署名は 4 つあります。ドキュメントを自分で確認できます。export function verify( トークン: 文字列、 // チェックが必要なトークン SecretOrPublicKey: シークレット | GetPublicKeyOrSecret、 // サーバー コールバックで定義された署名キー?: VerifyCallback<JwtPayload string>, // 検証情報を取得するためのコールバック result): void;
次に、応答したばかりtoken
リクエスト ヘッダーにコピーします。
### http://localhost:3000/afterlogin を取得します 権限: 保持者 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjUyNzg5NzA3LCJleHAiOjE2NTI3ODk3Mzd9.s9fk3YLhxTUcpUgCfIK4xQ N5 8Hk_XEP5y9GM9A8jBbY
以前のベアラー認証は、http プロトコルの標準の認証方法です。
また、 Send Request
をクリックすると、下の図のような応答が表示されたら、応答が成功したことを意味します。
実際、上記は JWT の簡単な使用例です。次に、JWT 自体の利点と欠点について説明します。
実際、JWT が占有するストレージ領域は小さくありません。署名する必要がある情報が多すぎると、トークンの長さが制限を超える可能性があります。たとえば、次の 2 つの図を比較してください。
明らかに、ペイロード内の情報量が増加すると、トークンの長さも増加します。
実際、 token
Cookie
も多くのスペースを占める場合、フロントエンドはlocalStorage localStorage
のローカル ストレージに保存できますが、それが配置されていないと問題が発生します。 Cookie を使用すると、セキュリティが大幅に低下します。js スクリプトを通じて取得する危険性があり、ハッカーはそれを使って何でもできることになります。
柔軟性のない適時性。実際、JWT にはユーザーtoken
永続的に保存する必要がないという点があり、先ほど見たように、 token
には有効期限も含まれています。有効期限が変更されると、 token
が改ざんされてしまいます。有効期限を保存して手動で変更する方法がないため、ユーザーが 2 回ログインして 2 つのtoken
が生成された場合、 token
すぐに削除することは困難です。原則として、生成される 2 つのtoken
すべて有効です。
上記では主にいくつかの点について説明します。
JWT の原理は主に、サーバーの秘密キーを使用して、JSON 署名によって生成されたtoken
と通信することです。
また、JWT の内部データの構成についても紹介します。JWT の内部データは、署名アルゴリズムとタイプを指定するためのヘッダー、JSON データを送信するためのペイロード、およびデータに対して署名アルゴリズムを実行して改ざんを防止するための署名によって使用されます。
nodejs を介して JWT を使用する方法、 sign
メソッドを使用してデータ署名を実行する方法、およびverify
メソッドを使用して署名検証を実行する方法について詳しく説明します。
JWT のいくつかの欠点も紹介します。
1 つは、署名データの量が増えるとストレージ容量が増加することです。
次に、セキュリティの問題があります。ストレージ容量が大きすぎると、比較的高いセキュリティ レベルのCookie
に保存されず、スクリプトが勝手に取得されてしまいます。
次に、 token
の適時性を柔軟に制御できない適時性があります。
これは参考用の上記のnodejsのデモソースコードです。