ヘッダーは自動的に送信されないため、オリジンは制御されます。このアプリケーションでは、CSRFトークンがCookieとしてクライアントに送信されるため、ブラウザーによって自動的に返送されますが、保護を提供するのはヘッダーです。」この質問に対する答えは、「ほとんど」使用します。認証とCSRF保護のためにセッションを使用することは、間違いなく良いことです。この状態はどこかに保存する必要があり、セッションから取り出す場合は、別の場所に置き、サーバーとクライアントの両方で手動で管理する必要があります。これは単にコードが増えただけであり、メンテナンスも増えただけであり、全体として完全に車輪を再発明したことになります。「しかし、しかし...」と答えるでしょう。「アプリケーションを今どのように水平にスケーリングするのですか?」これは上で尋ねていた「本当の」質問ですが、「セッション状態が悪い、ステートレスでなければなりません」に短縮される傾向があります。パニックにならないでください。ここで重要なのは、セキュリティがステートフルであることです。安全でステートレスなアプリケーションを持つことはできません。状態をどこに保存しますか?これですべてです。非常に有益でインサイトに富んだ講演を行い、状態の必要性(およびその普遍性-TCPとSSLはステートフルであるため、システムはそれを知っているかどうかに関係なくステートフルです)。このトピックをさらに詳しく調べます。良いニュースは、選択肢があることです。簡単な選択は、セッションデータをメモリに保存し、ロードバランサーのスティッキーセッションに依存して、同じセッションからのリクエストを同じJVMにルーティングすることです(すべてサポートしています)。これで十分に理解でき、非常に多くのユースケースで機能します。1つの選択肢は、アプリケーションのインスタンス間でセッションデータを共有することです。厳格でセキュリティデータのみを保存している限り、データは小さく、変更はめったに行われません(ユーザーがログインまたはログアウトしたとき、またはセッションがタイムアウトしたときのみ)。インフラストラクチャに大きな問題はありません。使用するのも簡単です。Sessionを使用するため、ここで設定する方法について詳しく説明する必要はありませんが、文字通り数行のコードとRedisサーバーであり、非常に高速です。デプロイし、それをRedisサービスにバインドすることです。カスタムトークンの実装(ステートレス、外観)はどうですか?それが最後のセクションへの応答であった場合、それをもう一度参照してください。トークンをどこかに保存した場合はおそらくステートレスではありませんが、保存していない場合(たとえば、JWTエンコードトークンを使用している場合)、CSRF保護をどのように提供しますか?それは重要です。Winchに起因)は次のとおりです。アプリケーションまたはAPIがブラウザーからアクセスされる場合、CSRF保護が必要です。セッションなしでそれができないということではなく、自分ですべてのコードを書かなければならないということです。CSRFを必要とせず、完全に「ステートレス」(非セッションベース)のトークン実装を持っていると判断したとしても、それを消費して使用するためにクライアントに追加のコードを記述する必要がありました。ブラウザーとサーバーの独自の組み込み機能:ブラウザーは常にCookieを送信し、サーバーは常にセッションを保持します(スイッチをオフにしない限り)。そのコードはビジネスロジックではなく、収益を上げるものではなく、単なるオーバーヘッドであるため、さらに悪いことにお金がかかります。現在のアプリケーションは、ユーザーがライブ環境の「実際の」アプリケーションに期待するものに近いものであり、おそらく、そのアーキテクチャを備えたより機能豊富なアプリケーション(静的な単一サーバーコンテンツとJSONリソース)。使用してセキュリティデータを保存し、クライアントが送信するCookieを考慮して使用することを頼りにしていますが、独自のビジネスドメインに集中できるため、それに満足しています。次のセクションでは、アーキテクチャを個別の認証およびUIサーバーに加えて、JSON用のスタンドアロンリソースサーバーに拡張します。これは明らかに、複数のリソースサーバーに簡単に一般化できます。Sessionをスタックに導入し、それを使用して認証データを共有する方法を示します。使用する方法について引き続き説明します。ここでは、アプリケーションの動的コンテンツとして使用している「あいさつ」リソースを、まず保護されていないリソースとして別のサーバーに分割し、次にOpaqueトークンで保護します。これは一連のセクションの3番目であり、アプリケーションの基本的な構成要素に追いつくか、最初のセクションを読んでゼロからビルドするか、Githubのソースコードに直接進むことができます。部分と、トークンによって保護されている部分(GitHub)。このセクションでサンプルアプリケーションを使用している場合は、ブラウザのキャッシュのCookieとHTTP基本認証情報を必ずクリアしてください。Chromeでは、単一サーバーでこれを行う最良の方法は、新しいシークレットウィンドウを開くことです。別のリソースサーバークライアント側の変更クライアント側では、リソースを別のバックエンドに移動するために行うことはあまりありません。「ホーム」コンポーネントは次のとおりです。}}これに必要なのは、URLを変更することだけです。例:localhostで新しいリソースを実行する場合、次のようになります。削除するだけです(「/resource」でした)。使用して行ったように、新しいリソースサーバーを作成する必要があります。Javaプロジェクト)をお気に入りのIDEにインポートするか、コマンドラインでファイルと「mvn」を操作するだけです。constructor}それが完了すると、アプリケーションはブラウザにロード可能になります。-Dserver.port=9000http://localhost:9000のブラウザーに移動すると、挨拶付きのJSONが表示されます。resources」内)でポートの変更をベイクできます。9000ブラウザのUI(ポート8080)からそのリソースをロードしようとすると、ブラウザがXHRリクエストを許可しないため、機能しないことがわかります。プロトコルに従ってリソースサーバーへのアクセスが許可されているかどうかを確認するために、リソースサーバーとネゴシエートしようとします。Angularの責任ではないため、Cookie契約と同様に、ブラウザ内のすべてのJavaScriptでこのように機能します。2つのサーバーは共通の発信元を宣言していないため、ブラウザーは要求の送信を拒否し、UIは壊れています。これを修正するには、「プリフライト」OPTIONSリクエストと呼び出し元の許可された動作をリストするヘッダーを含むCORSプロトコルをサポートする必要があります。いくつかあるため、コントローラーマッピングにアノテーションを追加するだけです。簡単に使用するのは簡単で汚れており、動作しますが、安全ではなく、決して推奨されません。新しいアーキテクチャーで動作するアプリケーションがあります。唯一の問題は、リソースサーバーにセキュリティがないことです。Securityの追加UIサーバーのように、フィルターレイヤーとしてリソースサーバーにセキュリティを追加する方法も確認できます。...</dependencies>リソースサーバーを再起動します。http://localhost:9000/login...curlはAngularクライアントと同じヘッダーを送信していないため、(ホワイトラベル)ログインページへのリダイレクトを取得しています。類似したヘッダーを送信するようにコマンドを変更します。Unauthorized...クライアントにすべてのリクエストで資格情報を送信するように教えるだけです。トークン認証インターネットおよび人々のSpringバックエンドプロジェクトには、カスタムトークンベースの認証ソリューションが散らばっています。Securityには正規の実装はありません。その理由の1つは、おそらくさらに簡単な方法があるからです。使用して認証データを保管することを思い出してください。セッションと直接対話することはありません。リソースサーバーで、UIによって認証が検証されたストアをそのリポジトリにポイントできる場合、2つのサーバー間で認証を共有する方法があります。HttpSession)があります。そのため、そのストアを配布してリソースサーバーに開くことができる場合、ほとんどのソリューションがあります。Filterをセットアップするためのサーバーの構成行です。いくつかの依存関係を追加する必要があります。Sessionは連携してRedisに接続し、セッションデータを一元的に保存します。その1行のコードとlocalhostで実行されているRedisサーバーを使用して、UIアプリケーションを実行し、有効なユーザー資格情報でログインすると、セッションデータ(認証)がredisに保存されます。簡単に起動できます(WindowsまたはMacOSではVMが必要です)。upを使用してコマンドラインで簡単に実行できます。指すようにアプリを構成する必要があります。UIからカスタムトークンを送信する不足している唯一の部分は、ストア内のデータへのキーの転送メカニズムです。IDであるため、UIクライアントでそのキーを取得できる場合、リソースサーバーにカスタムヘッダーとして送信できます。そのため、「ホーム」コントローラーは、グリーティングリソースのHTTPリクエストの一部としてヘッダーを送信するように変更する必要があります。使用して、リソースサーバーへのすべてのリクエストにヘッダーを追加することです。http://localhost:9000]」に直接移動する代わりに、「/token」のUIサーバー上の新しいカスタムエンドポイントへの呼び出しの成功コールバックでその呼び出しをラップしました。}}これで、UIアプリケーションの準備が整い、バックエンドへのすべての呼び出しに対して「X-Auth-Token」というヘッダーにセッションIDが含まれます。リソースサーバーでの認証リソースサーバーがカスタムヘッダーを受け入れることができるように、リソースサーバーに小さな変更が1つあります。CORS構成では、そのヘッダーをリモートクライアントからの許可されたヘッダーとして指定する必要があります。Securityに通過を許可することを伝える必要があります。アクセスする必要はありません。要求がプリフライトであることを認識していないため、機密データを誤って送信するハンドラーが存在する場合があります。構成ユーティリティは、フィルター層ですべてのプリフライトリクエストを処理することにより、これを軽減します。あとは、リソースサーバーでカスタムトークンを取得し、それを使用してユーザーを認証するだけです。Securityにセッションリポジトリの場所と、受信リクエストでトークン(セッションID)を探す場所を伝えるだけなので、これは非常に簡単です。Filterをセットアップできます。UIサーバーにあるもののミラーイメージであるため、Redisをセッションストアとして確立します。ブラウザが認証されていないクライアントでダイアログをポップアップしないようにする必要があります。401を送信するため、ブラウザはユーザー名とパスワードのダイアログで応答します。Securityはデフォルトでそれを処理します。リソースサーバーには、新しい認証スキームで動作するように、最後に1つの変更があります。application.properties)で明示的にする必要があります。Securityに対して「セッションを作成することはありませんが、セッションが存在する場合はセッションを使用する」と言います(UIでの認証のために既に存在します)。リソースサーバーを再起動し、新しいブラウザーウィンドウでUIを開きます。すべてがCookieで機能しないのですか?カスタムヘッダーを使用し、クライアントにヘッダーを入力するコードを記述する必要がありました。これはそれほど複雑ではありませんが、可能な限りCookieとセッションを使用するというパートIIのアドバイスと矛盾するようです。しないと、不必要な複雑さが追加されるという議論がありましたが、確かに、現在の実装はこれまで見てきた中で最も複雑です:ソリューションの技術的な部分は、ビジネスロジック(明らかに小さい)をはるかに上回っています。これは間違いなく公正な批判です(そして、このシリーズの次のセクションで説明する予定です)が、すべての目的でCookieとセッションを使用するほど単純ではない理由を簡単に見てみましょう。少なくともまだセッションを使用していますが、これは理にかなっています。SecurityとServletコンテナーは私たちの努力なしにそれを行う方法を知っているからです。Cookieを使用して認証トークンを転送し続けることができなかったでしょうか?それは素晴らしかったでしょうが、それが機能しない理由があり、ブラウザが私たちを認可しないということです。JavaScriptクライアントからブラウザーのCookieストアをいじってみることができますが、いくつかの制限があり、十分な理由があります。サーバーから「HttpOnly」として送信されたCookieにはアクセスできません(セッションCookieの場合、デフォルトで表示されます)。SessionのデフォルトCookie名)を設定できなかったため、カスタムの「X-Session」ヘッダーを使用する必要がありました。これらの制限は両方ともユーザー自身の保護のためのものであるため、悪意のあるスクリプトは適切な認可なしにリソースにアクセスできません。Sessionを使用して強制的にセッションを共有できます)。結論このシリーズのパートIIのアプリケーションの機能を複製しました。リモートバックエンドから取得した挨拶を含むホームページで、ナビゲーションバーにログインとログアウトのリンクがあります。違いは、グリーティングがUIサーバーに組み込まれているためはなく、スタンドアロンのリソースサーバーから送信されることです。ソリューションを使用できます。抽出することで、ソリューション100%を宣言型にすることもできます。この興味深いタスクは、次の2回の記事以降に延期します。Sessionを使用して、論理的に同じアプリケーションではない2つのサーバー間でセッションを共有しました。これは巧妙なトリックであり、「通常の」JEE分散セッションでは不可能です。使用する方法について引き続き説明します。Cloudを使用して認証とバックエンドリソースへのアクセスを制御するAPIゲートウェイを構築する方法を示します。使用してバックエンドリソースを認証する単純な分散アプリケーションを構築しました。これでは、UIサーバーをバックエンドリソースサーバーへのリバースプロキシにし、最後の実装(カスタムトークン認証によって導入された技術的な複雑さ)の課題を修正し、ブラウザークライアントからのアクセスを制御するための多くの新しいオプションを提供します。Basic資格情報を必ずクリアしてください。Chromeでは、単一サーバーでこれを行う最良の方法は、新しいシークレットウィンドウを開くことです。Gatewayは、フロントエンドクライアントの単一のエントリポイント(および制御)であり、ブラウザベース(このセクションの例のような)またはモバイルの場合があります。クライアントは1つのサーバーのURLを知るだけでよく、バックエンドを変更せずに自由にリファクタリングできます。これは大きな利点です。ログ記録などの利点があります。Cloudを使用すると、単純なリバースプロキシの実装は非常に簡単です。コードを順守している場合、最後のセクションの最後のアプリケーション実装が少し複雑であることがわかるため、それを繰り返すのに最適な場所ではありません。Securityで保護されていませんでした。別個のプロジェクトなので、そこから始めます。UIサーバーとリソースサーバーがあり、お互いに通信しています。Securityがまだないため、最初にシステムを動作させてから、そのレイヤーを追加できます。Gatewayに変換するには、UIサーバーに1つの小さな調整が必要です。アノテーションを追加する必要があります。"application.yml")にマップする必要があります。http://localhost:9000これは、「このサーバーのパターン/resource/**のパスを、localhost:9000のリモートサーバーの同じパスにマップする」ことを示しています。シンプルでありながら効果的です(OK。YAMLを含む6行ですが、必ずしも必要ではありません)!この作業を行うために必要なのは、クラスパス上の適切なものだけです。POMにいくつかの新しい行があります。BootのようなスターターPOMですが、このZuulプロキシに必要な依存関係を管理します。クライアントでプロキシを使用するこれらの変更を適用しても、アプリケーションは引き続き機能しますが、クライアントを変更するまで、実際には新しいプロキシを使用していません。幸いなことにそれは簡単です。最後のセクションの「単一」から「vanilla」サンプルに行った変更を元に戻す必要があります。Gateway)を介してリソースサーバーにプロキシされます。さらなる簡素化さらに良いことは、リソースサーバーでCORSフィルターが不要になることです。それを一緒に投げました、それは手で技術的に焦点を合わせたもの(特にセキュリティに関する場合)をしなければならなかったことは赤信号だったはずです。中間状態では、リソースサーバーにセキュリティが設定されていないことを覚えているかもしれません。余談:ネットワークアーキテクチャがアプリケーションアーキテクチャを反映している場合、ソフトウェアセキュリティの欠如は問題にならないかもしれません(リソースサーバーにUIサーバー以外の人が物理的にアクセスできないようにすることができます)。その簡単なデモンストレーションとして、リソースサーバーにローカルホストでのみアクセスできるようにします。データセンターでのみ表示されるネットワークアドレスを使用して、すべてのリソースサーバーとすべてのユーザーデスクトップで機能するセキュリティソリューションを実現します。ソフトウェアレベルでセキュリティが必要であると判断したと仮定します(多くの理由により、かなりの可能性があります)。問題になることはありません。<artifactId>spring-boot-starter-security</artifactId></dependency>セキュアなリソースサーバーを取得するにはこれで十分ですが、パートIIIにはなかったのと同じ理由で、まだ動作しているアプリケーションは取得できません。2つのサーバー間で共有認証状態はありません。以前のように、両方のサーバーに依存関係を追加します。宣言を両方に追加できるため、今回は構成がはるかに簡単になります。すべてのヘッダーを転送することを明示的に宣言します(つまり、「機密」はありません)。sensitive-headers:その後、リソースサーバーに移動できます。2つの小さな変更が必要です。Basicを明示的に無効にすることです(ブラウザが認証ダイアログをポップアップするのを防ぐため)。Basicを保持したまま、401チャレンジを「Basic」以外のものに変更します。1行の実装でそれを行うことができます。1つは、application.propertiesで非ステートレスセッション作成ポリシーを明示的に要求することです。使用してください)、システムは動作します。http://localhost:8080でUIのホームページをロードしてログインすると、バックエンドからのメッセージがホームページに表示されます。Gateway)でHTTPリクエストを確認できます。Sessionを使用しているため、Cookie名がわずかに異なる(「JSESSIONID」ではなく「SESSION」)ことを除いて、パートIIの最後のシーケンスと同じです。アーキテクチャは異なり、「/resource」に対する最後の要求はリソースサーバーにプロキシされたため、特別です。Actuatorから)を見ると、リバースプロキシの動作を確認できます。新しいブラウザでhttp://localhost:8080/traceに移動します(ブラウザ用のJSONプラグインをまだ入手していない場合は、ブラウザで見やすく読みやすくします)。Basic(ブラウザポップアップ)で認証する必要がありますが、ログインフォームと同じ資格情報が有効です。開始時またはその近くで、次のようなリクエストのペアが表示されます。認証のクロスオーバーの可能性がないように別のブラウザーを使用してみてください(たとえば、UIのテストにChromeを使用しない場合はFirefoxを使用します)-アプリの動作を停止しませんが、含まれている場合はトレースを読みにくくします。同じブラウザからの認証の混合。}},2番目のエントリは、クライアントから「/resource」のゲートウェイへのリクエストであり、Cookie(ブラウザによって追加)とCSRFヘッダー(パートIIで説明したようにAngularによって追加)を確認できます。あり、これはリソースサーバーへの呼び出しをトレースしていることを意味します。"/"に送信されたことがわかり、(重要な)CookieとCSRFヘッダーも送信されていることがわかります。Sessionがなければ、これらのヘッダーはリソースサーバーにとって意味がありませんが、設定方法では、これらのヘッダーを使用して認証およびCSRFトークンデータでセッションを再構成できます。リクエストは許可されており、ビジネスをしています!結論このセクションではかなり多くのことを説明しましたが、2台のサーバーに最小限のボイラープレートコードがあり、どちらも安全であり、ユーザーエクスペリエンスが損なわれない、本当に素晴らしい場所に到達しました。Cloudを読んで、より多くの機能をゲートウェイに簡単に追加する方法を確認してください。このシリーズの次のセクションでは、認証の責任を別のサーバーに抽出することで、アプリケーションアーキテクチャを少し拡張します(シングルサインオンパターン)。使用する方法について引き続き説明します。Gatewayを継承し、シングルサインオンとOAuth2トークン認証をバックエンドリソースに実行する方法を示します。Cloudを使用してUIサーバーに組み込みAPIゲートウェイを実装する小さな分散アプリケーションを構築しました。このセクションでは、認証サーバーへの多くのシングルサインオンアプリケーションの最初のUIサーバーを作成するために、認証の責任を別のサーバーに抽出します。これは、企業およびソーシャルスタートアップの両方で、最近の多くのアプリケーションで一般的なパターンです。OAuth2サーバーをオーセンティケーターとして使用するため、OAuth2サーバーを使用してバックエンドリソースサーバーのトークンを付与することもできます。Cloudはアクセストークンをバックエンドに自動的に中継し、UIサーバーとリソースサーバーの両方の実装をさらに簡素化できるようにします。Basic資格情報を必ずクリアしてください。Chromeでは、単一サーバーでこれを行う最良の方法は、新しいシークレットウィンドウを開くことです。最初のステップは、認証とトークン管理を処理する新しいサーバーを作成することです。始めることができます。Javaプロジェクト)をお気に入りのIDEにインポートするか、コマンドラインでファイルと「mvn」を操作するだけです。<artifactId>spring-security-oauth2</artifactId></dependency>認可サーバーの実装は非常に簡単です。最小バージョンは次のようになります。@EnableAuthorizationServerを追加した後)あと1つだけ行う必要があります。openid---これにより、クライアント「acme」がシークレットと「authorization_code」を含むいくつかの認可された認可タイプで登録されます。次に、テスト用の予測可能なパスワードを使用して、ポート9999で実行します。"/")を使用しないようにコンテキストパスを設定します。しないと、ローカルホスト上の他のサーバーのCookieが間違ったサーバーに送信される可能性があります。サーバーを実行すると、サーバーが機能していることを確認できます。メソッドを開始します。Basic認証によって保護されます。開始するには、認可エンドポイントにアクセスします。http://localhost:9999/uaa/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://example.comで認証されると、認証コードが添付されたexample.comへのリダイレクトが取得されます。例:http://example.com/?code=jYWioI(英語)このサンプルアプリケーションの目的のために、リダイレクトが登録されていないクライアント「acme」を作成しました。これにより、example.comへのリダイレクトを取得できます。本番アプリケーションでは、常にリダイレクトを登録する(およびHTTPSを使用する)必要があります。トークンエンドポイントの「acme」クライアント資格情報を使用して、コードをアクセストークンに交換できます。"2219199c...")であり、サーバー内のメモリ内トークンストアによってバックアップされます。現在のトークンが期限切れになったときに新しいアクセストークンを取得するために使用できるリフレッシュトークンも取得しました。「acme」クライアントに「パスワード」付与を認可したため、認証コードの代わりにcurlとユーザー資格情報を使用して、トークンエンドポイントからトークンを直接取得することもできます。これはブラウザベースのクライアントには適していませんが、テストには役立ちます。OAuthが提供するホワイトラベルUIが表示されます。後から戻って、パートIIの自己完結型サーバーの場合と同じように強化することができます。OAuthに置き換えることができます。SessionとRedisの依存関係も削除する必要があるため、これを置き換えます。OAuth2から)に置き換えます。Basicではなくアクセストークンを要求する準備ができましたが、実際にプロセスを完了するには構成の変更が必要です。