核心配置
Spring Boot 示例
Spring Boot 为 OAuth 2.0 登录提供了完整的自动配置功能。
本节展示了如何使用Google作为认证提供者来配置OAuth 2.0 登录示例,并涵盖以下主题:
初始设置
要使用 Google 的 OAuth 2.0 身份验证系统进行登录,您必须在 Google API 控制台中设置一个项目以获取 OAuth 2.0 凭据。
|
Google 的 OAuth 2.0 实现用于身份验证,符合OpenID Connect 1.0规范,并已通过OpenID 认证。 |
请按照OpenID Connect页面上的说明进行操作,从“设置 OAuth 2.0”部分开始。
完成“获取 OAuth 2.0 凭据”说明后,您应该会获得一个新的 OAuth 客户端,其凭据包括客户端 ID 和客户端密钥。
设置重定向 URI
重定向 URI 是应用程序中的路径,最终用户的用户代理在 Google 身份验证成功并在同意页面授予对 OAuth 客户端(在上一步中创建)的访问权限后,将被重定向回该路径。
在“设置重定向 URI”子章节中,请确保已授权的重定向 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-secretOAuth 客户端属性1 spring.security.oauth2.client.registration是 OAuth 客户端属性的基本属性前缀。2 在基本属性前缀之后是 ClientRegistration的 ID,例如 Google。 -
将
client-id和client-secret属性中的值替换为你之前创建的 OAuth 2.0 凭据。
启动应用程序
启动 Spring Boot 示例应用,并访问 localhost:8080。
随后,您将被重定向到默认的自动生成登录页面,该页面会显示一个 Google 登录链接。
点击 Google 链接,随后将被重定向到 Google 进行身份验证。
使用您的 Google 账号凭据完成身份验证后,您将看到同意屏幕。 该同意屏幕会要求您允许或拒绝之前创建的 OAuth 客户端访问您的信息。 点击允许,授权该 OAuth 客户端访问您的电子邮件地址和基本个人资料信息。
此时,OAuth 客户端会从用户信息端点(UserInfo Endpoint)获取您的电子邮件地址和基本个人资料信息,并建立一个已认证的会话。
Spring Boot 属性映射
下表列出了 Spring Boot OAuth 客户端属性与 ClientRegistration 属性之间的映射关系。
| Spring Boot | 客户端注册 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
通用 OAuth2 提供商
CommonOAuth2Provider 为多个知名提供商(包括 Google、GitHub、Facebook、X 和 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)与 GOOGLE 中的 enum CommonOAuth2Provider(不区分大小写)相匹配。 |
对于需要指定不同 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://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(): 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()
}
}
注册一个 SecurityFilterChain @Bean
以下示例展示了如何使用 SecurityFilterChain 注册一个 @Bean @EnableWebSecurity,并通过 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 {
authorizeHttpRequests {
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://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
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
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()
}
}
无需 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 {
authorizeHttpRequests {
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>