コア構成

Spring Boot サンプル

Spring Boot は、OAuth 2.0 ログインの完全な自動構成機能を提供します。

このセクションでは、Google を 認証プロバイダーとして使用して OAuth 2.0 ログイン WebFlux サンプル [GitHub] (英語) を構成する方法を示し、次のトピックについて説明します。

初期設定

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

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

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

リダイレクト URI の設定

リダイレクト URI は、エンドユーザーのユーザーエージェントが Google で認証され、同意ページで OAuth クライアント ( 前の手順で作成された ) へのアクセスが許可された後にリダイレクトされるアプリケーション内のパスです。

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

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

OAuth クライアントがプロキシサーバーの背後で実行されている場合は、プロキシサーバー構成をチェックして、アプリケーションが正しく構成されていることを確認することをお勧めします。また、redirect-uri でサポートされている  URI テンプレート変数も参照してください。

application.yml を構成する

Google を使用した新しい OAuth クライアントが作成されたため、認証フローに OAuth クライアントを使用するようにアプリケーションを構成する必要があります。そうするために:

  1. application.yml に移動して、次の構成を設定します。

    例 1: OAuth クライアントのプロパティ
    spring:
      security:
        oauth2:
          client:
            registration:	(1)
              google:	(2)
                client-id: google-client-id
                client-secret: google-client-secret
    1spring.security.oauth2.client.registration は、OAuth クライアントプロパティの基本プロパティプレフィックスです。
    2 ベースプロパティプレフィックスの後には、google などの ClientRegistration の ID が続きます。
  2. client-id および client-secret プロパティの値を、前に作成した OAuth 2.0 資格情報に置き換えます。

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

Spring Boot サンプルを起動し、localhost:8080 に移動します。次に、デフォルトの自動生成ログインページにリダイレクトされ、Google へのリンクが表示されます。

Google リンクをクリックすると、認証のために Google にリダイレクトされます。

Google アカウントの資格情報で認証した後、次のページに 同意 画面が表示されます。同意 画面では、前に作成した OAuth クライアントへのアクセスを認可するか拒否するかを尋ねられます。許可をクリックして、OAuth クライアントがメールアドレスと基本的なプロファイル情報にアクセスすることを承認します。

この時点で、OAuth クライアントは UserInfo エンドポイント (英語) からメールアドレスと基本プロファイル情報を取得し、認証済みセッションを確立します。

Spring Boot プロパティマッピング

次の表に、Spring Boot OAuth クライアントプロパティの ClientRegistration プロパティへのマッピングの概要を示します。

Spring BootClientRegistration

spring.security.oauth2.client.registration.[registrationId]

registrationId

spring.security.oauth2.client.registration.[registrationId].client-id

clientId

spring.security.oauth2.client.registration.[registrationId].client-secret

clientSecret

spring.security.oauth2.client.registration.[registrationId].client-authentication-method

clientAuthenticationMethod

spring.security.oauth2.client.registration.[registrationId].authorization-grant-type

authorizationGrantType

spring.security.oauth2.client.registration.[registrationId].redirect-uri

redirectUri

spring.security.oauth2.client.registration.[registrationId].scope

scopes

spring.security.oauth2.client.registration.[registrationId].client-name

clientName

spring.security.oauth2.client.provider.[providerId].authorization-uri

providerDetails.authorizationUri

spring.security.oauth2.client.provider.[providerId].token-uri

providerDetails.tokenUri

spring.security.oauth2.client.provider.[providerId].jwk-set-uri

providerDetails.jwkSetUri

spring.security.oauth2.client.provider.[providerId].issuer-uri

providerDetails.issuerUri

spring.security.oauth2.client.provider.[providerId].user-info-uri

providerDetails.userInfoEndpoint.uri

spring.security.oauth2.client.provider.[providerId].user-info-authentication-method

providerDetails.userInfoEndpoint.authenticationMethod

spring.security.oauth2.client.provider.[providerId].user-name-attribute

providerDetails.userInfoEndpoint.userNameAttributeName

ClientRegistration は、spring.security.oauth2.client.provider.[providerId].issuer-uri プロパティを指定することにより、OpenID Connect プロバイダーの構成エンドポイント (英語) または認可サーバーのメタデータエンドポイント [IETF] (英語) の検出を使用して最初に構成できます。

CommonOAuth2Provider

CommonOAuth2Provider は、Google、GitHub、Facebook、Okta などの多くのよく知られたプロバイダーのデフォルトのクライアントプロパティのセットを事前定義します。

例: authorization-uritoken-uriuser-info-uri は、プロバイダーでは頻繁に変更されません。必要な構成を減らすためにデフォルト値を提供することは理にかなっています。

前に示したように、Google クライアントを構成する場合、client-id プロパティと client-secret プロパティのみが必要です。

次のリストに例を示します。

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: google-client-id
            client-secret: google-client-secret
registrationId (google)は CommonOAuth2Provider の GOOGLEenum (大文字と小文字を区別しない)と一致するため、クライアントプロパティの自動デフォルト設定はここでシームレスに機能します。

google-login など、別の registrationId を指定する場合は、provider プロパティを構成することにより、クライアントプロパティの自動デフォルト設定を活用できます。

次のリストに例を示します。

spring:
  security:
    oauth2:
      client:
        registration:
          google-login:	(1)
            provider: google	(2)
            client-id: google-client-id
            client-secret: google-client-secret
1registrationId は google-login に設定されます。
2provider プロパティは google に設定され、CommonOAuth2Provider.GOOGLE.getBuilder() で設定されたクライアントプロパティの自動デフォルト設定を活用します。

カスタムプロバイダープロパティの構成

マルチテナンシーをサポートする OAuth 2.0 プロバイダーがいくつかあります。これにより、テナント(またはサブドメイン)ごとに異なるプロトコルエンドポイントが作成されます。

例: Okta に登録された OAuth クライアントは特定のサブドメインに割り当てられ、独自のプロトコルエンドポイントを持ちます。

これらの場合のために、Spring Boot はカスタムプロバイダープロパティを構成するための次の基本プロパティを提供します: spring.security.oauth2.client.provider.[providerId]

次のリストに例を示します。

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
        provider:
          okta:	(1)
            authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize
            token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token
            user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo
            user-name-attribute: sub
            jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys
1 基本プロパティ(spring.security.oauth2.client.provider.okta)により、プロトコルエンドポイントの場所をカスタム構成できます。

Spring Boot 自動構成のオーバーライド

OAuth クライアントをサポートするための Spring Boot 自動構成クラスは ReactiveOAuth2ClientAutoConfiguration です。

次のタスクを実行します。

  • 構成された OAuth クライアントプロパティからの ClientRegistration で構成される ReactiveClientRegistrationRepository@Bean を登録します。

  • SecurityWebFilterChain@Bean を登録し、serverHttpSecurity.oauth2Login() を介して OAuth 2.0 ログインを有効にします。

特定の要件に基づいて自動構成をオーバーライドする必要がある場合、次の方法でオーバーライドできます。

ReactiveClientRegistrationRepository @Bean を登録する

次の例は、ReactiveClientRegistrationRepository@Bean を登録する方法を示しています。

  • Java

  • Kotlin

@Configuration
public class OAuth2LoginConfig {

	@Bean
	public ReactiveClientRegistrationRepository clientRegistrationRepository() {
		return new InMemoryReactiveClientRegistrationRepository(this.googleClientRegistration());
	}

	private ClientRegistration googleClientRegistration() {
		return ClientRegistration.withRegistrationId("google")
				.clientId("google-client-id")
				.clientSecret("google-client-secret")
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
				.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
				.scope("openid", "profile", "email", "address", "phone")
				.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
				.tokenUri("https://www.googleapis.com/oauth2/v4/token")
				.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
				.userNameAttributeName(IdTokenClaimNames.SUB)
				.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
				.clientName("Google")
				.build();
	}
}
@Configuration
class OAuth2LoginConfig {

    @Bean
    fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {
        return InMemoryReactiveClientRegistrationRepository(googleClientRegistration())
    }

    private fun googleClientRegistration(): ClientRegistration {
        return ClientRegistration.withRegistrationId("google")
                .clientId("google-client-id")
                .clientSecret("google-client-secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                .scope("openid", "profile", "email", "address", "phone")
                .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
                .tokenUri("https://www.googleapis.com/oauth2/v4/token")
                .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
                .userNameAttributeName(IdTokenClaimNames.SUB)
                .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
                .clientName("Google")
                .build()
    }
}

SecurityWebFilterChain @Bean を登録する

次の例は、SecurityWebFilterChain@Bean を @EnableWebFluxSecurity に登録し、serverHttpSecurity.oauth2Login() を介して OAuth 2.0 ログインを有効にする方法を示しています。

OAuth2 ログイン構成
  • Java

  • Kotlin

@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
		http
			.authorizeExchange(authorize -> authorize
				.anyExchange().authenticated()
			)
			.oauth2Login(withDefaults());

		return http.build();
	}
}
@Configuration
@EnableWebFluxSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http {
            authorizeExchange {
                authorize(anyExchange, authenticated)
            }
            oauth2Login { }
        }

        return http.build()
    }
}

自動構成を完全にオーバーライドする

次の例は、ReactiveClientRegistrationRepository@Bean と SecurityWebFilterChain@Bean を登録することにより、自動構成を完全にオーバーライドする方法を示しています。

自動構成のオーバーライド
  • Java

  • Kotlin

@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginConfig {

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
		http
			.authorizeExchange(authorize -> authorize
				.anyExchange().authenticated()
			)
			.oauth2Login(withDefaults());

		return http.build();
	}

	@Bean
	public ReactiveClientRegistrationRepository clientRegistrationRepository() {
		return new InMemoryReactiveClientRegistrationRepository(this.googleClientRegistration());
	}

	private ClientRegistration googleClientRegistration() {
		return ClientRegistration.withRegistrationId("google")
				.clientId("google-client-id")
				.clientSecret("google-client-secret")
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
				.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
				.scope("openid", "profile", "email", "address", "phone")
				.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
				.tokenUri("https://www.googleapis.com/oauth2/v4/token")
				.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
				.userNameAttributeName(IdTokenClaimNames.SUB)
				.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
				.clientName("Google")
				.build();
	}
}
@Configuration
@EnableWebFluxSecurity
class OAuth2LoginConfig {

    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http {
            authorizeExchange {
                authorize(anyExchange, authenticated)
            }
            oauth2Login { }
        }

        return http.build()
    }

    @Bean
    fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {
        return InMemoryReactiveClientRegistrationRepository(googleClientRegistration())
    }

    private fun googleClientRegistration(): ClientRegistration {
        return ClientRegistration.withRegistrationId("google")
                .clientId("google-client-id")
                .clientSecret("google-client-secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                .scope("openid", "profile", "email", "address", "phone")
                .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
                .tokenUri("https://www.googleapis.com/oauth2/v4/token")
                .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
                .userNameAttributeName(IdTokenClaimNames.SUB)
                .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
                .clientName("Google")
                .build()
    }
}

Spring Boot を使用しない Java 構成

Spring Boot を使用できず、CommonOAuth2Provider の事前定義プロバイダーの 1 つ (Google など) を構成したい場合は、次の構成を適用します。

OAuth2 ログイン構成
  • Java

  • Kotlin

@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginConfig {

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
		http
			.authorizeExchange(authorize -> authorize
				.anyExchange().authenticated()
			)
			.oauth2Login(withDefaults());

		return http.build();
	}

	@Bean
	public ReactiveClientRegistrationRepository clientRegistrationRepository() {
		return new InMemoryReactiveClientRegistrationRepository(this.googleClientRegistration());
	}

	@Bean
	public ReactiveOAuth2AuthorizedClientService authorizedClientService(
			ReactiveClientRegistrationRepository clientRegistrationRepository) {
		return new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
	}

	@Bean
	public ServerOAuth2AuthorizedClientRepository authorizedClientRepository(
			ReactiveOAuth2AuthorizedClientService authorizedClientService) {
		return new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
	}

	private ClientRegistration googleClientRegistration() {
		return CommonOAuth2Provider.GOOGLE.getBuilder("google")
				.clientId("google-client-id")
				.clientSecret("google-client-secret")
				.build();
	}
}
@Configuration
@EnableWebFluxSecurity
class OAuth2LoginConfig {

    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http {
            authorizeExchange {
                authorize(anyExchange, authenticated)
            }
            oauth2Login { }
        }

        return http.build()
    }

    @Bean
    fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {
        return InMemoryReactiveClientRegistrationRepository(googleClientRegistration())
    }

    @Bean
    fun authorizedClientService(
        clientRegistrationRepository: ReactiveClientRegistrationRepository
    ): ReactiveOAuth2AuthorizedClientService {
        return InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository)
    }

    @Bean
    fun authorizedClientRepository(
        authorizedClientService: ReactiveOAuth2AuthorizedClientService
    ): ServerOAuth2AuthorizedClientRepository {
        return AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService)
    }

    private fun googleClientRegistration(): ClientRegistration {
        return CommonOAuth2Provider.GOOGLE.getBuilder("google")
                .clientId("google-client-id")
                .clientSecret("google-client-secret")
                .build()
    }
}