Приклад на Spring Boot 2.x

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

У цьому розділі показано, як налаштувати приклад коду для входу в систему в OAuth 2.0, використовуючи Google як постачальника аутентифікації, а також розглянуті наступні теми:

  • Початкове налаштування

  • Налаштування URI для переадресації

  • Конфігуруємо файл application.yml

  • Завантаження програми

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><