核心配置
Spring Boot 範例
Spring Boot 為 OAuth 2.0 登入帶來完整的自動配置功能。
本節說明如何使用 Google 作為身份驗證提供者來配置 OAuth 2.0 登入範例,並涵蓋以下主題
初始設定
若要使用 Google 的 OAuth 2.0 身份驗證系統進行登入,您必須在 Google API Console 中設定專案以取得 OAuth 2.0 憑證。
Google 的 OAuth 2.0 身份驗證實作 符合 OpenID Connect 1.0 規範,並已通過 OpenID 認證。 |
請按照 OpenID Connect 頁面上的說明進行操作,從「設定 OAuth 2.0」章節開始。
完成「取得 OAuth 2.0 憑證」的說明後,您應該會有一個新的 OAuth Client,其憑證包含 Client ID 和 Client Secret。
設定重新導向 URI
重新導向 URI 是應用程式中的路徑,終端使用者的使用者代理程式在透過 Google 身份驗證並在同意頁面上授予對 OAuth Client(在上一步中建立)的存取權後,將重新導向回該路徑。
在「設定重新導向 URI」小節中,請確保「授權的重新導向 URI」欄位設定為 localhost:8080/login/oauth2/code/google
。
預設的重新導向 URI 範本為 |
如果 OAuth Client 在 Proxy 伺服器後方執行,您應該檢查 Proxy 伺服器配置,以確保應用程式已正確配置。另請參閱 |
配置 application.yml
現在您已經有一個新的 Google OAuth Client,您需要配置應用程式以使用 OAuth Client 進行身份驗證流程。若要執行此操作
-
前往
application.yml
並設定以下配置spring: security: oauth2: client: registration: (1) google: (2) client-id: google-client-id client-secret: google-client-secret
OAuth Client 屬性1 spring.security.oauth2.client.registration
是 OAuth Client 屬性的基本屬性前綴。2 在基本屬性前綴之後是 ClientRegistration
的 ID,例如 Google。 -
將
client-id
和client-secret
屬性中的值替換為您先前建立的 OAuth 2.0 憑證。
啟動應用程式
啟動 Spring Boot 範例並前往 localhost:8080
。然後您將被重新導向至預設的自動產生登入頁面,其中顯示 Google 的連結。
點擊 Google 連結,然後您將被重新導向至 Google 進行身份驗證。
使用您的 Google 帳戶憑證進行身份驗證後,您會看到同意畫面。同意畫面會詢問您是否允許或拒絕存取您先前建立的 OAuth Client。點擊「允許」以授權 OAuth Client 存取您的電子郵件地址和基本個人資料資訊。
此時,OAuth Client 會從 UserInfo Endpoint 檢索您的電子郵件地址和基本個人資料資訊,並建立已驗證的 Session。
Spring Boot 屬性映射
下表概述了 Spring Boot OAuth Client 屬性到 ClientRegistration 屬性的映射。
Spring Boot | ClientRegistration |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
您可以透過指定 |
CommonOAuth2Provider
CommonOAuth2Provider
為許多知名的提供者預先定義了一組預設的 client 屬性:Google、GitHub、Facebook 和 Okta。
例如,對於提供者來說,authorization-uri
、token-uri
和 user-info-uri
通常不會經常變更。因此,提供預設值以減少所需的配置是有意義的。
如先前所示範的,當我們 配置 Google client 時,只需要 client-id
和 client-secret
屬性。
以下清單顯示一個範例
spring:
security:
oauth2:
client:
registration:
google:
client-id: google-client-id
client-secret: google-client-secret
client 屬性的自動預設功能在此處無縫運作,因為 registrationId (google ) 與 CommonOAuth2Provider 中的 GOOGLE enum (不區分大小寫)匹配。 |
對於您可能想要指定不同的 registrationId
(例如 google-login
)的情況,您仍然可以透過配置 provider
屬性來利用 client 屬性的自動預設功能。
以下清單顯示一個範例
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() 中設定的 client 屬性的自動預設功能。 |
配置自訂提供者屬性
有些 OAuth 2.0 提供者支援多租戶,這會導致每個租戶(或子網域)有不同的協定端點。
例如,向 Okta 註冊的 OAuth Client 會指派給特定的子網域,並擁有自己的協定端點。
對於這些情況,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 Client 支援的 Spring Boot 自動配置類別是 OAuth2ClientAutoConfiguration
。
它執行以下任務
-
從配置的 OAuth Client 屬性中註冊由
ClientRegistration
組成的ClientRegistrationRepository
@Bean
。 -
註冊
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://127.0.0.1/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://127.0.0.1/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://127.0.0.1/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://127.0.0.1/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>