Spring Boot と OAuth2

このガイドでは、OAuth 2.0 [IETF] (英語) および Spring Boot を使用して「ソーシャルログイン」でさまざまなことを行うサンプルアプリを作成する方法を示します。

シンプルなシングルプロバイダーシングルサインオンから始まり、認証プロバイダーを選択したクライアント(GitHub (英語) または Google (英語) )まで機能します。

サンプルはすべて、バックエンドで Spring Boot と Spring Security を使用するシングルページアプリです。また、フロントエンドではプレーンな jQuery (英語) を使用しています。ただし、別の JavaScript フレームワークに変換したり、サーバー側レンダリングを使用したりするために必要な変更は最小限です。

すべてのサンプルは、Spring Boot (英語) のネイティブ OAuth 2.0 サポートを使用して実装されます。

いくつかのサンプルが相互に構築されており、各ステップで新しい機能が追加されています。

  • simple: ホームページと Spring Boot の OAuth 2.0 構成プロパティを介した無条件のログインを備えた非常に基本的な静的アプリ(ホームページにアクセスすると、自動的に GitHub にリダイレクトされます)。

  • click: ユーザーがログインするためにクリックする必要がある明示的なリンクを追加します。

  • logout: 認証されたユーザーのログアウトリンクも追加します。

  • two-providers: ユーザーがホームページで使用するものを選択できるように、2 番目のログインプロバイダーを追加します。

  • custom-error: 認証されていないユーザー向けのエラーメッセージと、GitHub の API に基づくカスタム認証を追加します。

機能ラダー内のあるアプリから次のアプリに移行するために必要な変更は、ソースコード [GitHub] (英語) で追跡できます。アプリの各バージョンは独自のディレクトリであるため、違いを比較できます。

各アプリは IDE にインポートできます。SocialApplication で main メソッドを実行してアプリを起動できます。それらはすべて、http://localhost:8080 にホームページを作成します (ログインしてコンテンツを表示するには、少なくとも GitHub および Google アカウントを持っている必要があります)。

また、mvn spring-boot:run を使用して、または jar ファイルを作成して mvn package および java -jar target/*.jar で実行することにより、コマンドラインですべてのアプリを実行できます(Spring Boot ドキュメント (英語) およびその他の利用可能なドキュメントに従って)。最上位でラッパー [GitHub] (英語) を使用する場合、Maven をインストールする必要はありません。たとえば

$ cd simple
$ ../mvnw package
$ java -jar target/*.jar
アプリはすべて、そのアドレスの GitHub および Google に登録された OAuth 2.0 クライアントを使用するため、localhost:8080 で動作します。別のホストまたはポートでアプリを実行するには、そのようにアプリを登録する必要があります。デフォルト値を使用する場合、ローカルホストを超えて資格情報が漏洩する危険はありません。ただし、何をインターネットに公開するかには注意し、独自のアプリ登録をパブリックソース管理に置かないでください。

GitHub でのシングルサインオン

このセクションでは、認証に GitHub を使用する最小限のアプリケーションを作成します。これは、Spring Boot の自動構成機能を利用することにより、非常に簡単になります。

新しいプロジェクトを作成する

まず、Spring Boot アプリケーションを作成する必要がありますが、これはさまざまな方法で実行できます。最も簡単な方法は、https://start.spring.io に移動して空のプロジェクトを生成することです(開始点として "Web" 依存関係を選択します)。同様に、コマンドラインでこれを実行します。

$ mkdir ui && cd ui
$ curl https://start.spring.io/starter.tgz -d style=web -d name=simple | tar -xzvf -

その後、そのプロジェクトをお気に入りの IDE(デフォルトでは通常の Maven Java プロジェクト)にインポートするか、コマンドラインでファイルと mvn を操作するだけです。

ホームページを追加する

新しいプロジェクトで、src/main/resources/static フォルダーに index.html を作成します。結果が次のようになるように、スタイルシートと JavaScript リンクを追加する必要があります。

index.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <title>Demo</title>
    <meta name="description" content=""/>
    <meta name="viewport" content="width=device-width"/>
    <base href="/"/>
    <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css"/>
    <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
    <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
	<h1>Demo</h1>
	<div class="container"></div>
</body>
</html>

OAuth 2.0 ログイン機能をデモンストレーションするためにこれは必要ありませんが、最後に快適な UI を用意するのは良いことなので、ホームページの基本的なものから始めることもできます。

アプリを起動してホームページを読み込むと、スタイルシートが読み込まれていないことに気が付くでしょう。そのため、jQuery と Twitter Bootstrap を追加して、これらも追加する必要があります。

pom.xml
<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>jquery</artifactId>
	<version>3.4.1</version>
</dependency>
<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>bootstrap</artifactId>
	<version>4.3.1</version>
</dependency>
<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>webjars-locator-core</artifactId>
</dependency>

最後の依存関係は、webjars サイトによってライブラリとして提供される webjars「ロケーター」です。Spring はロケーターを使用して、正確なバージョンを知る必要なく webjar 内の静的アセットを見つけることができます(したがって、index.html のバージョンレス /webjars/** リンク)。MVC 自動構成をオフにしない限り、Spring Boot アプリでは webjar ロケーターがデフォルトでアクティブになります。

これらの変更が適切に行われると、アプリの見栄えの良いホームページが作成されます。

GitHub および Spring Security を使用したアプリケーションの保護

アプリケーションを安全にするために、Spring Security を依存関係として単純に追加できます。「ソーシャル」ログイン(GitHub へのデリゲート)を行うため、Spring Security OAuth 2.0 Client スターターを含める必要があります。

pom.xml
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

それを追加することで、デフォルトで OAuth 2.0 でアプリを保護します。

次に、GitHub を認証プロバイダーとして使用するようにアプリを構成する必要があります。これを実現するには、次を実行します。

新しい GitHub アプリを追加する

ログインに GitHub の OAuth 2.0 認証システムを使用するには、最初に新しい GitHub アプリを追加する (英語) を行う必要があります。

新しい OAuth アプリを選択すると、新しい OAuth アプリケーションの登録ページが表示されます。アプリの名前と説明を入力します。次に、アプリのホームページ(この場合は http://localhost:8080)を入力します。最後に、認可コールバック URL を http://localhost:8080/login/oauth2/code/github として指定し、アプリケーションの登録をクリックします。

OAuth リダイレクト URI は、エンドユーザーのユーザーエージェントが GitHub で認証され、アプリケーションの承認ページでアプリケーションへのアクセスを認可した後にリダイレクトされるアプリケーション内のパスです。

デフォルトのリダイレクト URI テンプレートは {baseUrl}/login/oauth2/code/{registrationId} です。registrationId は、ClientRegistration の一意の識別子です。

application.yml を構成する

次に、GitHub へのリンクを作成するには、以下を application.yml に追加します。

application.yml
spring:
  security:
    oauth2:
      client:
        registration:
          github:
            clientId: github-client-id
            clientSecret: github-client-secret
# ...

GitHub で作成したばかりの OAuth 2.0 資格情報を使用し、github-client-id をクライアント ID に、github-client-secret をクライアントシークレットに置き換えるだけです。

アプリケーションを起動する

この変更により、アプリを再度実行して http://localhost:8080 のホームページにアクセスできます。これで、ホームページの代わりに、GitHub でのログインにリダイレクトされるはずです。それを行い、行うように求められた認可を受け入れると、ローカルアプリにリダイレクトされ、ホームページが表示されます。

GitHub にログインしたままにすると、Cookie やキャッシュデータのない新しいブラウザーで開いても、このローカルアプリで再認証する必要はありません。(それがシングルサインオンの意味です。)

このセクションでサンプルアプリケーションを使用している場合は、ブラウザーのキャッシュの Cookie と HTTP 基本認証情報を必ずクリアしてください。単一のサーバーでこれを行う最良の方法は、新しいプライベートウィンドウを開くことです。

ローカルで実行されているアプリのみがトークンを使用でき、要求されるスコープが制限されているため、このサンプルへのアクセスを許可しても安全です。ただし、このようなアプリにログインするときは、承認する内容に注意してください。アプリは、あなたに必要以上の許可を求める場合があります (たとえば、個人データを変更する許可を求めるかもしれませんが、これはあなたの不利益になる可能性があります)。

何が起こっているのか?

OAuth 2.0 の用語で記述したアプリはクライアントアプリケーションであり、認証コード付与 [IETF] (英語) を使用して GitHub(認証サーバー)からアクセストークンを取得します。

次に、アクセストークンを使用して、ログイン ID や名前などの個人的な詳細(許可した操作のみ)を GitHub に要求します。このフェーズでは、GitHub はリソースサーバーとして機能し、送信したトークンをデコードし、ユーザーの詳細へのアクセスをアプリに許可するかどうかを確認します。そのプロセスが成功すると、アプリはユーザーの詳細を Spring Security コンテキストに挿入して、ユーザーが認証されるようにします。

ブラウザーツール(Chrome または Firefox の F12)を調べて、すべてのホップのネットワークトラフィックを追跡すると、GitHub で前後にリダイレクトが表示され、最終的に新しい Set-Cookie でホームページに戻ります。ヘッダー。この Cookie(デフォルトでは JSESSIONID)は、Spring(またはサーブレットベースの)アプリケーションの認証詳細のトークンです。

ユーザーが外部プロバイダー(GitHub)で認証する必要があるコンテンツを表示するという意味で、安全なアプリケーションがあります。

インターネットバンキングの Web サイトにこれを使用したくないでしょう。しかし、基本的な識別の目的のため、サイトの異なるユーザー間でコンテンツを分離するために、優れた出発点です。そのため、この種の認証は最近非常に人気があります。

次のセクションでは、アプリケーションにいくつかの基本的な機能を追加します。また、GitHub への最初のリダイレクトを取得したときに何が起こっているのかをユーザーに少しわかりやすくします。

ようこそページを追加する

このセクションでは、GitHub でログインするための明示的なリンクを追加して、作成した単純なアプリを変更します。すぐにリダイレクトされる代わりに、新しいリンクがホームページに表示され、ユーザーはログインするか、認証されないままにするかを選択できます。ユーザーがリンクをクリックした場合にのみ、安全なコンテンツがレンダリングされます。

ホームページの条件付きコンテンツ

ユーザーが認証されているという条件でコンテンツをレンダリングするには、サーバー側またはクライアント側のレンダリングのオプションがあります。

ここでは、JQuery (英語) を使用してクライアント側を変更しますが、他のものを使用する場合は、クライアントコードを変換するのはそれほど難しくないはずです。

動的コンテンツを開始するには、次のような HTML 要素をいくつかマークする必要があります。

index.html
<div class="container unauthenticated">
    With GitHub: <a href="/oauth2/authorization/github">click here</a>
</div>
<div class="container authenticated" style="display:none">
    Logged in as: <span id="user"></span>
</div>

デフォルトでは、最初の <div> が表示され、2 番目の <div> は表示されません。id 属性を持つ空の <span> にも注意してください。

すぐに、ログインしたユーザーの詳細を JSON として返すサーバー側のエンドポイントを追加します。

ただし、最初に、そのエンドポイントにヒットする次の JavaScript を追加します。エンドポイントのレスポンスに基づいて、この JavaScript は <span> タグにユーザー名を入力し、<div> を適切に切り替えます。

index.html
<script type="text/javascript">
    $.get("/user", function(data) {
        $("#user").html(data.name);
        $(".unauthenticated").hide()
        $(".authenticated").show()
    });
</script>

この JavaScript は、サーバー側のエンドポイントが /user と呼ばれることを期待していることに注意してください。

/user エンドポイント

ここで、上記のサーバー側のエンドポイントを追加して、/user と呼びます。現在ログインしているユーザーを送り返します。これは、メインクラスで非常に簡単に実行できます。

SocialApplication.java
@SpringBootApplication
@RestController
public class SocialApplication {

    @GetMapping("/user")
    public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
        return Collections.singletonMap("name", principal.getAttribute("name"));
    }

    public static void main(String[] args) {
        SpringApplication.run(SocialApplication.class, args);
    }

}

ハンドラーメソッドに挿入された @RestController@GetMappingOAuth2User の使用に注意してください。

エンドポイントに OAuth2User 全体を返すことは、ブラウザークライアントには公開したくない情報が含まれている可能性があるため、素晴らしいアイデアではありません。

ホームページを公開する

最後に 1 つの変更が必要です。

これで、このアプリは以前と同様に正常に機能し、認証されますが、ページを表示する前にリダイレクトされます。リンクを表示するには、WebSecurityConfigurerAdapter を継承してホームページのセキュリティをオフにする必要もあります。

SocialApplication
@SpringBootApplication
@RestController
public class SocialApplication extends WebSecurityConfigurerAdapter {

    // ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	// @formatter:off
        http
            .authorizeRequests(a -> a
                .antMatchers("/", "/error", "/webjars/**").permitAll()
                .anyRequest().authenticated()
            )
            .exceptionHandling(e -> e
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
            )
            .oauth2Login();
        // @formatter:on
    }

}

Spring Boot は、@SpringBootApplication アノテーションが付けられたクラスの WebSecurityConfigurerAdapter に特別な意味を付加します。これを使用して、OAuth 2.0 認証プロセッサーを運ぶセキュリティフィルターチェーンを構成します。

上記の構成は、許可されたエンドポイントのホワイトリストを示し、他のすべてのエンドポイントは認証を必要とします。

許可したい:

  • /。これは動的に作成したページであり、そのコンテンツの一部は認証されていないユーザーにも表示されるためです。

  • /error。これは、エラーを表示するための Spring Boot エンドポイント

  • /webjars/** は、認証されているかどうかにかかわらず、すべての訪問者に対して JavaScript を実行するためです。

ただし、この構成では /user については何も表示されません。/user を含むすべてのものは、最後の .anyRequest().authenticated() 構成のために示されない限り、安全なままです。

最後に、Ajax を介してバックエンドとインターフェースするため、ログインページにリダイレクトするデフォルトの動作ではなく、401 で応答するようにエンドポイントを構成する必要があります。authenticationEntryPoint を構成することでこれが実現します。

これらの変更が完了すると、アプリケーションが完成します。アプリケーションを実行してホームページにアクセスすると、「GitHub でログイン」への適切なスタイルの HTML リンクが表示されます。このリンクは、GitHub に直接ではなく、認証を処理する(および GitHub にリダイレクトを送信する)ローカルパスに移動します。認証が完了すると、ローカルアプリにリダイレクトされ、名前が表示されるようになります(GitHub でそのデータへのアクセスを許可するようにアクセス許可を設定した場合)。

ログアウトボタンを追加する

このセクションでは、ユーザーがアプリからログアウトできるボタンを追加して、作成したクリックアプリを変更します。これは単純な機能のように見えますが、実装には多少の注意が必要なので、正確に行う方法を議論するのに時間をかける価値があります。ほとんどの変更は、アプリを読み取り専用リソースから読み取り / 書き込みリソースに変換しているという事実に関係しているため(ログアウトには状態の変更が必要です)、実際のアプリケーションでは同じ変更が必要になります。単なる静的コンテンツではありません。

クライアント側の変更

クライアントでは、ログアウトボタンといくつかの JavaScript を提供するだけで、サーバーにコールバックして認証のキャンセルを要求できます。まず、UI の「認証済み」セクションで、ボタンを追加します。

index.html
<div class="container authenticated">
  Logged in as: <span id="user"></span>
  <div>
    <button onClick="logout()" class="btn btn-primary">Logout</button>
  </div>
</div>

そして、JavaScript で参照する logout() 関数を提供します。

index.html
var logout = function() {
    $.post("/logout", function() {
        $("#user").html('');
        $(".unauthenticated").show();
        $(".authenticated").hide();
    })
    return true;
}

logout() 関数は /logout に対して POST を実行し、動的コンテンツをクリアします。これで、サーバー側に切り替えてそのエンドポイントを実装できます。

ログアウトエンドポイントの追加

Spring Security は、/logout エンドポイントのサポートを組み込んでいます。これにより、正しい処理が行われます(セッションをクリアし、Cookie を無効にします)。エンドポイントを構成するには、WebSecurityConfigurerAdapter の既存の configure() メソッドを単純に拡張します。

SocialApplication.java
@Override
protected void configure(HttpSecurity http) throws Exception {
	// @formatter:off
    http
        // ... existing code here
        .logout(l -> l
            .logoutSuccessUrl("/").permitAll()
        )
        // ... existing code here
    // @formatter:on
}

/logout エンドポイントは、POST する必要があり、ユーザーをクロスサイトリクエストフォージェリ(CSRF、「シーサーフ」と発音)から保護するには、トークンをリクエストに含める必要があります。トークンの値は現在のセッションにリンクされており、これが保護を提供しているため、JavaScript アプリにそのデータを取得する方法が必要です。

多くの JavaScript フレームワークには CSRF のサポートが組み込まれていますが (たとえば、Angular では XSRF と呼ばれています)、多くの場合、Spring Security の標準の動作とは少し異なる方法で実装されています。たとえば、Angular では、フロントエンドはサーバーから "XSRF-TOKEN" という Cookie が送信されることを望んでおり、それを検出すると、その値を "X-XSRF-TOKEN" というヘッダーとして返します。同じ動作をシンプルな jQuery クライアントで実装すると、サーバー側の変更は、変更をまったく行わず、またはほとんど行わずに他のフロントエンド実装でも機能します。Spring Security にこれを教えるには、Cookie を作成するフィルターを追加する必要があります。

WebSecurityConfigurerAdapter では、次のことを行います。

SocialApplication.java
@Override
protected void configure(HttpSecurity http) throws Exception {
	// @formatter:off
    http
        // ... existing code here
        .csrf(c -> c
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
        )
        // ... existing code here
    // @formatter:on
}

クライアントに CSRF トークンを追加する

このサンプルでは高レベルのフレームワークを使用していないため、バックエンドから Cookie として使用可能にした CSRF トークンを明示的に追加する必要があります。コードを少し単純にするために、js-cookie ライブラリをインクルードします。

pom.xml
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>js-cookie</artifactId>
    <version>2.1.0</version>
</dependency>

次に、HTML で参照できます。

index.html
<script type="text/javascript" src="/webjars/js-cookie/js.cookie.js"></script>

最後に、XHR で Cookies コンビニエンスメソッドを使用できます。

index.html
$.ajaxSetup({
  beforeSend : function(xhr, settings) {
    if (settings.type == 'POST' || settings.type == 'PUT'
        || settings.type == 'DELETE') {
      if (!(/^http:.*/.test(settings.url) || /^https:.*/
        .test(settings.url))) {
        // Only send the token to relative URLs i.e. locally.
        xhr.setRequestHeader("X-XSRF-TOKEN",
          Cookies.get('XSRF-TOKEN'));
      }
    }
  }
});

準備完了 !

これらの変更が完了したら、アプリを実行して新しいログアウトボタンを試す準備ができました。アプリを起動し、新しいブラウザーウィンドウにホームページを読み込みます。「ログイン」リンクをクリックして、GitHub に移動します(すでにログインしている場合は、リダイレクトに気付かない場合があります)。ログアウトボタンをクリックして、現在のセッションをキャンセルし、アプリを認証されていない状態に戻します。興味がある場合は、ブラウザーがローカルサーバーと交換するリクエストで新しい Cookie とヘッダーを確認できるはずです。

ログアウトエンドポイントがブラウザークライアントで動作するようになりました。他のすべての HTTP リクエスト(POST、PUT、DELETE など)も同様に動作することを覚えておいてください。これは、より現実的な機能を備えたアプリケーションに適したプラットフォームになるはずです。

GitHub でログイン

このセクションでは、すでに構築したログアウトアプリを変更し、ステッカーページを追加して、エンドユーザーが複数の資格情報セットから選択できるようにします。

エンドユーザーの 2 番目のオプションとして Google を追加しましょう。

初期設定

ログインに Google の OAuth 2.0 認証システムを使用するには、Google API コンソールでプロジェクトを設定し、OAuth 2.0 資格情報を取得する必要があります。

「OAuth 2.0 のセットアップ」セクションから始まる OpenID Connect (英語) ページの指示に従ってください。

「OAuth 2.0 資格情報の取得」の手順を完了すると、クライアント ID とクライアントシークレットで構成される資格情報を持つ新しい OAuth クライアントが必要になります。

リダイレクト URI の設定

また、以前に GitHub に対して行ったように、リダイレクト URI を提供する必要があります。

「リダイレクト URI の設定」サブセクションで、承認されたリダイレクト URI フィールドが http://localhost:8080/login/oauth2/code/google に設定されていることを確認します。

クライアント登録の追加

次に、Google を指すようにクライアントを構成する必要があります。Spring Security は複数のクライアントを念頭に置いて構築されているため、GitHub 用に作成した資格情報と並行して Google 資格情報を追加できます。

application.yml
spring:
  security:
    oauth2:
      client:
        registration:
          github:
            clientId: github-client-id
            clientSecret: github-client-secret
          google:
            client-id: google-client-id
            client-secret: google-client-secret

ご覧のとおり、Google は、Spring Security がすぐに使用できるサポートを提供するもう 1 つのプロバイダーです。

クライアントでは、変更は簡単です - 別のリンクを追加するだけです:

index.html
<div class="container unauthenticated">
  <div>
    With GitHub: <a href="/oauth2/authorization/github">click here</a>
  </div>
  <div>
    With Google: <a href="/oauth2/authorization/google">click here</a>
  </div>
</div>
URL の最終パスは、application.yml のクライアント登録 ID と一致する必要があります。
Spring Security には、/oauth2/authorization/{registrationId} ではなく /login をポイントすることでアクセスできるデフォルトのプロバイダー選択ページが付属しています。

ローカルユーザーデータベースを追加する方法

多くのアプリケーションは、認証が外部プロバイダーに委譲されている場合でも、ユーザーに関するデータをローカルに保持する必要があります。ここではコードを示していませんが、2 つのステップで簡単に実行できます。

  1. データベースのバックエンドを選択し、ニーズに合ったカスタム User オブジェクトのリポジトリ(たとえば Spring Data を使用)をセットアップし、外部認証から完全または部分的にデータを取り込むことができます。

  2. OAuth2UserService を実装して公開し、データベースと同様に認可サーバーを呼び出します。実装はデフォルトの実装に委譲できます。これにより、認可サーバーを呼び出すという手間がかかります。実装は、カスタム User オブジェクトを継承し、OAuth2User を実装するものを返す必要があります。

ヒント: User オブジェクトにフィールドを追加して、外部プロバイダーの一意の識別子にリンクします(ユーザーの名前ではなく、外部プロバイダーのアカウントに固有のもの)。

認証されていないユーザー用のエラーページの追加

このセクションでは、以前に作成した 2 つのプロバイダーアプリを変更して、認証できないユーザーにフィードバックを提供します。同時に、認証ロジックを継承して、ユーザーが特定の GitHub 組織に属している場合にのみユーザーを許可するルールを含めます。「組織」は GitHub ドメイン固有の概念ですが、同様のルールが他のプロバイダーにも考案される可能性があります。例: Google では、特定のドメインのユーザーのみを認証したい場合があります。

GitHub への切り替え

2 プロバイダーのサンプルでは、GitHub を OAuth 2.0 プロバイダーとして使用しています。

application.yml
spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: bd1c0a783ccdd1c9b9e4
            client-secret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
          # ...

クライアントでの認証失敗の検出

クライアントでは、認証できなかったユーザーにフィードバックを提供することができます。これを容易にするために、最終的に情報メッセージを追加する div を追加できます。

index.html
<div class="container text-danger error"></div>

次に、/error エンドポイントに呼び出しを追加し、<div> に結果を入力します。

index.html
$.get("/error", function(data) {
    if (data) {
        $(".error").html(data);
    } else {
        $(".error").html('');
    }
});

エラー関数は、表示するエラーがあるかどうかをバックエンドで確認します

エラーメッセージを追加する

エラーメッセージの取得をサポートするには、認証が失敗したときにキャプチャーする必要があります。これを実現するために、次のように AuthenticationFailureHandler を構成できます。

protected void configure(HttpSecurity http) throws Exception {
	// @formatter:off
	http
	    // ... existing configuration
	    .oauth2Login(o -> o
            .failureHandler((request, response, exception) -> {
			    request.getSession().setAttribute("error.message", exception.getMessage());
			    handler.onAuthenticationFailure(request, response, exception);
            })
        );
}

上記は、認証が失敗するたびにエラーメッセージをセッションに保存します。

次に、次のような単純な /error コントローラーを追加できます。

SocialApplication.java
@GetMapping("/error")
public String error(HttpServletRequest request) {
	String message = (String) request.getSession().getAttribute("error.message");
	request.getSession().removeAttribute("error.message");
	return message;
}
これにより、アプリのデフォルトの /error ページが置き換えられます。これは、このケースでは問題ありませんが、ニーズに合わせて十分に洗練されていない場合があります。

サーバーで 401 を生成する

ユーザーが GitHub でログインできない、またはログインしたくない場合は、Spring Security から 401 レスポンスがすでに送信されるため、認証に失敗した場合(トークンの付与を拒否するなど)、アプリはすでに動作しています。

少し物事を盛り上げるために、適切な組織にいないユーザーを拒否するように認証ルールを継承できます。

GitHub API を使用してユーザーの詳細を調べることができるため、認証プロセスの適切な部分にプラグインするだけです。

幸い、Spring Boot はそのような単純な使用例のために、簡単な拡張ポイントを提供しています。型 OAuth2UserService の @Bean を宣言すると、ユーザープリンシパルを識別するために使用されます。そのフックを使用して、ユーザーが正しい組織にいることをアサートし、そうでない場合は例外をスローできます。

SocialApplication.java
@Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService(WebClient rest) {
    DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
    return request -> {
        OAuth2User user = delegate.loadUser(request);
        if (!"github".equals(request.getClientRegistration().getRegistrationId())) {
        	return user;
        }

        OAuth2AuthorizedClient client = new OAuth2AuthorizedClient
                (request.getClientRegistration(), user.getName(), request.getAccessToken());
        String url = user.getAttribute("organizations_url");
        List<Map<String, Object>> orgs = rest
                .get().uri(url)
                .attributes(oauth2AuthorizedClient(client))
                .retrieve()
                .bodyToMono(List.class)
                .block();

        if (orgs.stream().anyMatch(org -> "spring-projects".equals(org.get("login")))) {
            return user;
        }

        throw new OAuth2AuthenticationException(new OAuth2Error("invalid_token", "Not in Spring Team", ""));
    };
}

このコードは、認証されたユーザーに代わって GitHub API にアクセスするための WebClient インスタンスに依存していることに注意してください。それが完了すると、組織をループして、"spring-projects" (これは Spring オープンソースプロジェクトを格納するために使用される組織)に一致する組織を探します。正常に認証できるようにしたいが、Spring エンジニアリングチームに所属していない場合は、そこで独自の値に置き換えることができます。一致するものがない場合は、OAuth2AuthenticationException がスローされ、これが Spring Security によって取得され、401 レスポンスになります。

WebClient も Bean として作成する必要がありますが、spring-boot-starter-oauth2-client を使用したことでその成分がすべて自動で書き込めるため、簡単です。

@Bean
public WebClient rest(ClientRegistrationRepository clients, OAuth2AuthorizedClientRepository authz) {
    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 =
            new ServletOAuth2AuthorizedClientExchangeFilterFunction(clients, authz);
    return WebClient.builder()
            .filter(oauth2).build();
}
明らかに、上記のコードは他の認証ルールに一般化でき、一部は GitHub に、一部は他の OAuth 2.0 プロバイダーに適用できます。必要なのは、WebClient とプロバイダーの API の知識だけです。

結論

Spring Boot と Spring Security を使用して、非常に少ない労力で多くのスタイルのアプリを構築する方法を見てきました。すべてのサンプルで実行されるメインテーマは、外部 OAuth 2.0 プロバイダーを使用した認証です。

サンプルアプリケーションはすべて、通常は構成ファイルを変更するだけで、より具体的なユースケースに合わせて簡単に拡張および再構成できます。独自のサーバーでサンプルのバージョンを使用して GitHub(または同様の)に登録し、独自のホストアドレスのクライアント資格情報を取得する場合は覚えておいてください。そして、それらの資格情報をソース管理に入れないことを忘れないでください!

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語)

すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。

コードを入手する