Spring BootでGoogleログイン(OAuth2)を実装する方法


JWTでの自前認証を実装した次のステップとして、外部のGoogleアカウントでログインできるようにしたいですよね。Spring Security OAuth2 Clientを使えば、驚くほど少ないコードでGoogleソーシャルログインが実現できます。

この記事では、Google Cloud ConsoleでのOAuth2クライアント作成から、application.ymlの設定、SecurityFilterChainの構成、ユーザー情報の取得まで、ローカル環境で動かしながら解説します。

OAuth2認可コードフローの全体像

実装に入る前に、認可コードフローの流れを把握しておきましょう。

1. ブラウザ → Spring Boot: /oauth2/authorization/google にアクセス
2. Spring Boot → ブラウザ: Googleの認可エンドポイントへリダイレクト
3. ブラウザ → Google: ユーザーがログインして権限を許可
4. Google → Spring Boot: 認可コード(code)をリダイレクトURIに返す
5. Spring Boot → Google: code をアクセストークンに交換(バックチャンネル)
6. Spring Boot → Google: UserInfoエンドポイントでユーザー情報を取得
7. Spring Boot: セッションにユーザー情報を保存してログイン完了

JWTの自前発行と大きく違うのは、 ステップ5〜7をSpring Securityがほぼ自動で処理してくれる 点です。自分でトークン交換のリクエストを書く必要はありません。

Google Cloud ConsoleでOAuth2クライアントを作成する

まず client-idclient-secret を取得します。

  1. Google Cloud Console でプロジェクトを作成
  2. 「APIとサービス」→「OAuth同意画面」を設定(テスト用途なら「外部」でOK)
  3. 「認証情報」→「OAuthクライアントID」を作成
    • アプリケーションの種類は「ウェブアプリケーション」を選択
    • 承認済みリダイレクトURIに http://localhost:8080/login/oauth2/code/google を追加
  4. 表示された client-idclient-secret をメモ

リダイレクトURIのパス /login/oauth2/code/google はSpring Security OAuth2 Clientのデフォルト値です。最初はこのまま使うのが楽です。

プロジェクトのセットアップ

spring-boot-starter-oauth2-client を依存関係に追加します。

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
}

このスターターには spring-security-oauth2-clientspring-security-oauth2-jose(IDトークン検証用)が含まれています。

application.ymlにOAuth2設定を記述する

application.yml に以下を追加します。

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: ${GOOGLE_CLIENT_ID}
            client-secret: ${GOOGLE_CLIENT_SECRET}
            scope:
              - openid
              - email
              - profile

scopeopenid を含めることで、レスポンスが OidcUser として扱われ、メールアドレスや名前を簡単に取り出せます。provider の定義は不要です。Spring BootがGoogleの認証エンドポイントを自動で解決してくれます。

client-idclient-secret は必ず環境変数にしましょう。ソースコードに直書きしてGitにプッシュしてしまう事故が起きやすいので要注意です。

SecurityFilterChainでoauth2Login()を有効化する

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/", "/login").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .defaultSuccessUrl("/dashboard", true)
            );
        return http.build();
    }
}

oauth2Login() を呼ぶだけでGoogleログインが有効になります。defaultSuccessUrl でログイン後のリダイレクト先を指定できます。

CSRFはデフォルトで有効のままにしておきましょう。OAuth2フローにはstateパラメータによるCSRF対策が組み込まれており、Spring Securityが自動で処理してくれます。

ログイン後のユーザー情報を取得する

コントローラーで @AuthenticationPrincipal を使ってユーザー情報を受け取ります。

@RestController
public class UserController {

    @GetMapping("/dashboard")
    public Map<String, Object> dashboard(@AuthenticationPrincipal OidcUser user) {
        return Map.of(
            "name",    user.getAttribute("name"),
            "email",   user.getAttribute("email"),
            "picture", user.getAttribute("picture")
        );
    }
}

openid スコープを含めているので OidcUser としてキャストできます。openid を外した場合は OAuth2User になります。サービス層など @AuthenticationPrincipal が使えない場所では SecurityContextHolder.getContext().getAuthentication() から取得できます。

ログイン成功後の処理をカスタマイズする

初回ログイン時にユーザー情報をDBに保存したいケースは多いですよね。その場合は AuthenticationSuccessHandler を実装します。

@Component
public class OAuth2LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private final UserService userService;

    public OAuth2LoginSuccessHandler(UserService userService) {
        super("/dashboard");
        this.userService = userService;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        OidcUser oidcUser = (OidcUser) authentication.getPrincipal();
        userService.saveOrUpdate(oidcUser.getEmail(), oidcUser.getAttribute("name"));
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

SecurityConfig 側では .oauth2Login(oauth2 -> oauth2.successHandler(successHandler)) で登録します。

セッションベース認証とJWTとの違い

今回の実装は セッションベース です。Spring Security OAuth2 ClientはデフォルトでOAuth2AuthorizedClientをHttpSessionに保存します。

複数サーバーにスケールアウトする場合はセッション共有(Redisなど)が必要になります。StatelessなAPIにしたい場合は、認可コードフロー完了後に自前のJWTを発行するアーキテクチャが一般的です。そちらは Spring Security + JWTでステートレス認証を実装する方法 を参照してください。

動作確認

アプリを起動して http://localhost:8080 にアクセスすると、Spring Securityのデフォルトログインページが表示されます。「Sign in with Google」リンクをクリックするとGoogleの認証画面に遷移し、ログイン後に /dashboard に戻ってきます。

redirect_uri_mismatch が出た場合は、Google Cloud ConsoleのリダイレクトURIが http://localhost:8080/login/oauth2/code/google になっているか確認しましょう。403エラーが出る場合は SecurityFilterChainpermitAll() 設定を見直してみてください。

他プロバイダへの対応も簡単です。registrationgithub キーを追加するだけで動きます。Spring BootはGitHub・Facebook・Oktaなどをデフォルトでサポートしています。

まとめ

Spring Security OAuth2 Clientを使ったGoogleログインは、依存追加と application.yml の設定、oauth2Login() の一行でほぼ動きます。フローの複雑さと比べると、コード量の少なさが際立ちますよね。

Basic認証やJWTとの比較は Spring BootでBasic認証を実装する方法JWTでステートレス認証を実装する方法 も合わせて読んでみてください。フロントエンドと組み合わせる際は Spring BootのCORS設定ガイド も参考になります。