Пример на Spring Boot 2.x

Spring Boot 2.x предусматривает возможность полноценно использовать автоконфигурацию для настройки входа в систему через OAuth 2.0.

В этом разделе показано, как сконфигурировать пример кода для входа в систему в OAuth 2.0, используя Google в качестве поставщика аутентификации, а также рассмотрены следующие темы:

  • Начальная настройка

  • Настройка URI для переадресации

  • Конфигурируем файл application.yml

  • <Загрузка приложения< p>

Initial setup

Чтобы использовать систему аутентификации Google в OAuth 2.0 для входа в систему, необходимо настроить проект в Google API Console для получения учетных данных OAuth 2.0.

Реализация OAuth 2.0 через Google для аутентификации соответствует спецификации OpenID Connect 1.0 и сертифицирована OpenID.

Следуйте инструкциям на странице OpenID Connect, начиная с раздела "Настройка OAuth 2.0".

После выполнения инструкции по "Получению учетных данных OAuth 2.0" должен появиться новый клиент OAuth с учетными данными, состоящими из идентификатора клиента и секрета клиента.

Настройка URI для переадресации

URI для переадресации – это путь в приложении, на который перенаправляется пользовательский агент конечного пользователя после аутентификации в Google и предоставления доступа к OAuth-клиенту (созданного на предыдущем этапе) на странице Согласия.

В подразделе "Установка URI для переадресации" убедитесь, что в поле авторизованных URI для переадресации установлено значение localhost:8080/login/oauth2/code/google.

Шаблон URI для переадресации по умолчанию – {baseUrl}/login/oauth2/code/{registrationId}. RegistrationId – это уникальный идентификатор для ClientRegistration.
Если клиент OAuth работает за прокси-сервером, рекомендуется проверить конфигурацию прокси-сервера, чтобы убедиться, что приложение было сконфигуировано корректно. Также ознакомьтесь с поддерживаемыми переменными шаблона URI для redirect-uri.

Конфигурируем файл application.yml

Теперь, когда получен новый OAuth-клиент через Google, необходимо сконфигурировать приложение на использование OAuth-клиента для потока аутентификации. Для этого:

  1. Перейдите в application.yml и установите следующую конфигурацию:

    spring:
      security:
        oauth2:
          client:
            registration:	
              google:	
                client-id: google-client-id
                client-secret: google-client-secret
    Свойства OAuth-клиента
    1. spring.security.oauth2.client.registration – это базовый префикс свойств для свойств OAuth-клиента.
    2. После префикса базового свойства следует идентификатор для ClientRegistration, например google.
  2. Замените значения в свойствах client-id и client-secret на учетные данные OAuth 2.0, которые были созданы ранее.

Загрузка приложения

Запустите пример Spring Boot 2.x и перейдите на localhost:8080. Затем вы будете перенаправлены на автоматически созданную стандартную страницу входа в систему, на которой отображается ссылка для Google.

Нажмите на ссылку Google, после чего вы будете перенаправлены в Google для аутентификации.

После аутентификации с помощью учетных данных аккаунта Google на следующей странице появится экран Согласия. На экране "Согласие" будет предложено разрешить или запретить доступ к OAuth-клиенту, который был создан ранее. Нажмите "Разрешить", чтобы разрешить OAuth-клиенту доступ к вашему адресу электронной почты и основной информации профиля.

На этом этапе OAuth-клиент получает ваш адрес электронной почты и основную информацию о профиле из конечной точки UserInfo и устанавливает аутентифицированную сессию.

Отображения свойств в Spring Boot 2.x

В следующей таблице описано отображение свойств Spring Boot 2.x OAuth Client на свойства ClientRegistration.

Spring Boot 2.x ClientRegistration

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 можно изначально сконфигурировать с помощью обнаружения конечной точки конфигурации поставшика OpenID Connect или конечной точки метаданных сервера авторизации, задав свойство spring.security.oauth2.client.provider.[providerId].issuer-uri.

CommonOAuth2Provider

CommonOAuth2Provider предопределяет набор стандартных свойств клиента для ряда известных поставщиков: Google, GitHub, Facebook и Okta.

Например, authorization-uri, token-uri и user-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) совпадает с enum GOOGLE (без учета регистра) в CommonOAuth2Provider.

Для случаев, когда необходимо задать другой registrationId, например, google-login, все равно можно использовать автоматическое определение свойств клиента по умолчанию, сконфигурировав свойство provider.

В следующем листинге приведен пример:

spring:
  security:
    oauth2:
      client:
        registration:
          google-login:	
            provider: google	
            client-id: google-client-id
            client-secret: google-client-secret
  1. Для registrationId установлено значение google-login.
  2. Свойство provider установлено в значение google, что позволит использовать автоматическое определение свойств клиента по умолчанию, установленное в CommonOAuth2Provider.GOOGLE.getBuilder().

Конфигурирование кастомных свойств поставщика

Некоторые поставщики OAuth 2.0 поддерживают работу в режиме коллективной аренды, что приводит к созданию различных конечных точек протоколов для каждого арендатора (или поддомена).

Например, OAuth-клиент, зарегистрированный в Okta, присваивается определенному поддомену и имеет свои собственные конечные точки протоколов.

Для таких случаев Spring Boot 2.x предусматривает следующее базовое свойство для конфигурирования кастомных свойств поставщика: spring.security.oauth2.client.provider.[providerId].

В следующем листинге приведен пример:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
        provider:
          okta:	
            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 2.x

Класс автоконфигурации Spring Boot 2.x для поддержки OAuth-клиента – OAuth2ClientAutoConfiguration.

Он выполняет следующие задачи:

  • Регистрирует @Bean для ClientRegistrationRepository, состоящий из экземпляра(ов) ClientRegistration из сконфигурированных свойств OAuth-клиента.

  • Регистрирует @Bean для SecurityFilterChain и активирует авторизацию в OAuth 2.0 через httpSecurity.oauth2Login().

Если необходимо переопределить автоконфигурацию под конкретные потребности, то сделать это можно следующими способами:

  • Регистрируем @Bean для ClientRegistrationRepository

  • Регистрируем @Bean для SecurityFilterChain

  • Полное переопределение автоконфигурации

Регистрируем @Bean для ClientRegistrationRepository

В следующем примере показано, как зарегистрировать @Bean для ClientRegistrationRepository:

Java
@Configuration
public class OAuth2LoginConfig {
	@Bean
	public ClientRegistrationRepository clientRegistrationRepository() {
		return new InMemoryClientRegistrationRepository(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();
	}
}
Kotlin
@Configuration
class OAuth2LoginConfig {
    @Bean
    fun clientRegistrationRepository(): ClientRegistrationRepository {
        return InMemoryClientRegistrationRepository(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()
    }
}

Регистрируем @Bean для SecurityFilterChain

В следующем примере показано, как зарегистрировать @Bean для SecurityFilterChain с использованием аннотации @EnableWebSecurity и активировать авторизацию в OAuth 2.0 через httpSecurity.oauth2Login():

Конфигурация входа в систему в OAuth2
Java
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login(withDefaults());
		return http.build();
	}
}
Kotlin
@EnableWebSecurity
class OAuth2LoginSecurityConfig {
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login { }
        }
        return http.build()
    }
}

Полное переопределение автоконфигурации

В следующем примере показано, как полностью переопределить автоконфигурацию, зарегистрировав @Bean для ClientRegistrationRepository и @Bean для SecurityFilterChain.

Переопределение автоконфигурации
Java
@Configuration
public class OAuth2LoginConfig {
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login(withDefaults());
		return http.build();
	}
	@Bean
	public ClientRegistrationRepository clientRegistrationRepository() {
		return new InMemoryClientRegistrationRepository(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();
	}
}
Kotlin
@Configuration
class OAuth2LoginConfig {
    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login { }
        }
        return http.build()
    }
    @Bean
    fun clientRegistrationRepository(): ClientRegistrationRepository {
        return InMemoryClientRegistrationRepository(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()
    }
}

Конфигурация на базе Java без Spring Boot 2.x

Если использовать Spring Boot 2.x не представляется возможным, но нужно настроить одного из предопределенных поставщиков в CommonOAuth2Provider (например, Google), примените следующую конфигурацию:

Конфигурация входа в систему в OAuth2
Java
@EnableWebSecurity
public class OAuth2LoginConfig {
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login(withDefaults());
		return http.build();
	}
	@Bean
	public ClientRegistrationRepository clientRegistrationRepository() {
		return new InMemoryClientRegistrationRepository(this.googleClientRegistration());
	}
	@Bean
	public OAuth2AuthorizedClientService authorizedClientService(
			ClientRegistrationRepository clientRegistrationRepository) {
		return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
	}
	@Bean
	public OAuth2AuthorizedClientRepository authorizedClientRepository(
			OAuth2AuthorizedClientService authorizedClientService) {
		return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
	}
	private ClientRegistration googleClientRegistration() {
		return CommonOAuth2Provider.GOOGLE.getBuilder("google")
			.clientId("google-client-id")
			.clientSecret("google-client-secret")
			.build();
	}
}
Kotlin
@EnableWebSecurity
open class OAuth2LoginConfig {
    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login { }
        }
        return http.build()
    }
    @Bean
    open fun clientRegistrationRepository(): ClientRegistrationRepository {
        return InMemoryClientRegistrationRepository(googleClientRegistration())
    }
    @Bean
    open fun authorizedClientService(
        clientRegistrationRepository: ClientRegistrationRepository?
    ): OAuth2AuthorizedClientService {
        return InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository)
    }
    @Bean
    open fun authorizedClientRepository(
        authorizedClientService: OAuth2AuthorizedClientService?
    ): OAuth2AuthorizedClientRepository {
        return AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService)
    }
    private fun googleClientRegistration(): ClientRegistration {
        return CommonOAuth2Provider.GOOGLE.getBuilder("google")
            .clientId("google-client-id")
            .clientSecret("google-client-secret")
            .build()
    }
}
Xml
<http auto-config="true">
	<intercept-url pattern="/**" access="authenticated"/>
	<oauth2-login authorized-client-repository-ref="authorizedClientRepository"/>
</http>
<client-registrations>
	<client-registration registration-id="google"
						 client-id="google-client-id"
						 client-secret="google-client-secret"
						 provider-id="google"/>
</client-registrations>
<b:bean id="authorizedClientService"
		class="org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService"
		autowire="constructor"/>
<b:bean id="authorizedClientRepository"
		class="org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository">
	<b:constructor-arg ref="authorizedClientService"/>
</b:bean>