核心配置
Spring Boot 示例
Spring Boot 為 OAuth 2.0 登入提供了完整的自動配置功能。
本節演示瞭如何配置OAuth 2.0 登入示例,使用 Google 作為認證提供者,並涵蓋以下主題:
初始設定
要使用 Google 的 OAuth 2.0 認證系統進行登入,您必須在 Google API Console 中設定一個專案以獲取 OAuth 2.0 憑據。
Google 的 OAuth 2.0 認證實現符合 OpenID Connect 1.0 規範,並已獲得 OpenID 認證。 |
按照 OpenID Connect 頁面上的說明進行操作,從“Setting up OAuth 2.0”(設定 OAuth 2.0)部分開始。
完成“Obtain OAuth 2.0 credentials”(獲取 OAuth 2.0 憑據)說明後,您將獲得一個新的 OAuth 客戶端,其憑據包括客戶端 ID 和客戶端 Secret。
設定重定向 URI
重定向 URI 是應用中的路徑,終端使用者的使用者代理在透過 Google 認證並在同意頁面上授予 OAuth 客戶端(在上一步中建立)訪問許可權後,將被重定向回該路徑。
在“Set a redirect URI”(設定重定向 URI)子部分中,確保“Authorized redirect URIs”(授權重定向 URI)欄位設定為 localhost:8080/login/oauth2/code/google
。
預設的重定向 URI 模板是 |
配置 application.yml
現在您已經擁有了一個新的 Google OAuth 客戶端,您需要配置應用以使用該 OAuth 客戶端進行認證流程。具體步驟如下:
-
轉到
application.yml
並設定以下配置:spring: security: oauth2: client: registration: (1) google: (2) client-id: google-client-id client-secret: google-client-secret
OAuth 客戶端屬性1 spring.security.oauth2.client.registration
是 OAuth 客戶端屬性的基本屬性字首。2 基本屬性字首後面是 ClientRegistration
的 ID,例如 Google。 -
用您之前建立的 OAuth 2.0 憑據替換
client-id
和client-secret
屬性中的值。
啟動應用
啟動 Spring Boot 示例並訪問 localhost:8080
。然後您將被重定向到預設的自動生成的登入頁面,該頁面顯示一個 Google 連結。
點選 Google 連結,然後您將被重定向到 Google 進行認證。
使用您的 Google 賬戶憑據認證後,您將看到同意介面。同意介面會詢問您是否允許或拒絕訪問您之前建立的 OAuth 客戶端。點選允許以授權 OAuth 客戶端訪問您的電子郵件地址和基本資料資訊。
此時,OAuth 客戶端會從 UserInfo Endpoint 獲取您的電子郵件地址和基本資料資訊,並建立一個認證會話。
Spring Boot 屬性對映
下表概述了 Spring Boot OAuth 客戶端屬性與 ClientRegistration 屬性的對映關係。
Spring Boot | ClientRegistration |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 ) 與 CommonOAuth2Provider 中的 GOOGLE 列舉(不區分大小寫)匹配。 |
如果您想指定不同的 registrationId
,例如 google-login
,您仍然可以透過配置 provider
屬性來利用客戶端屬性的自動預設設定。
以下列出了一個示例:
spring:
security:
oauth2:
client:
registration:
google-login: (1)
provider: google (2)
client-id: google-client-id
client-secret: google-client-secret
1 | registrationId 設定為 google-login 。 |
2 | provider 屬性設定為 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 自動配置
Spring Boot 用於 OAuth 客戶端支援的自動配置類是 OAuth2ClientAutoConfiguration
。
它執行以下任務:
-
註冊一個
ClientRegistrationRepository
@Bean
,該 Bean 由配置的 OAuth 客戶端屬性中的ClientRegistration
(s) 組成。 -
註冊一個
SecurityFilterChain
@Bean
並透過httpSecurity.oauth2Login()
啟用 OAuth 2.0 登入。
如果您需要根據您的特定需求覆蓋自動配置,您可以透過以下方式進行:
註冊 ClientRegistrationRepository @Bean
以下示例演示瞭如何註冊 ClientRegistrationRepository
@Bean
-
Java
-
Kotlin
@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://#/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(): 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://#/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()
}
}
註冊 SecurityFilterChain @Bean
以下示例演示瞭如何使用 @EnableWebSecurity
註冊 SecurityFilterChain
@Bean
並透過 httpSecurity.oauth2Login()
啟用 OAuth 2.0 登入
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2Login(withDefaults());
return http.build();
}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(anyRequest, authenticated)
}
oauth2Login { }
}
return http.build()
}
}
完全覆蓋自動配置
以下示例演示瞭如何透過註冊 ClientRegistrationRepository
@Bean
和 SecurityFilterChain
@Bean
來完全覆蓋自動配置。
-
Java
-
Kotlin
@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://#/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
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://#/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
中預定義的提供者之一(例如 Google),請應用以下配置:
-
Java
-
Kotlin
-
Xml
@Configuration
@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();
}
}
@Configuration
@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()
}
}
<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>