核心配置
Spring Boot 示例
Spring Boot 为 OAuth 2.0 登录提供了完整的自动配置功能。
本节展示了如何使用Google作为身份验证提供程序配置OAuth 2.0 登录 WebFlux 示例,并涵盖以下主题
初始设置
要使用 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
并设置以下配置示例 1. OAuth 客户端属性spring: security: oauth2: client: registration: (1) google: (2) client-id: google-client-id client-secret: google-client-secret
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 客户端将从用户信息端点检索您的电子邮件地址和基本个人资料信息,并建立一个经过身份验证的会话。
Spring Boot 属性映射
下表概述了 Spring Boot OAuth 客户端属性与客户端注册属性的映射关系。
Spring Boot | 客户端注册 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 自动配置
OAuth 客户端支持的 Spring Boot 自动配置类是ReactiveOAuth2ClientAutoConfiguration
。
它执行以下任务
-
注册一个
ReactiveClientRegistrationRepository
@Bean
,该@Bean
由配置的 OAuth 客户端属性中的ClientRegistration
组成。 -
注册一个
SecurityWebFilterChain
@Bean
并通过serverHttpSecurity.oauth2Login()
启用 OAuth 2.0 登录。
如果需要根据您的特定需求覆盖自动配置,您可以通过以下方式进行
注册 ReactiveClientRegistrationRepository @Bean
以下示例显示了如何注册ReactiveClientRegistrationRepository
@Bean
-
Java
-
Kotlin
@Configuration
public class OAuth2LoginConfig {
@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryReactiveClientRegistrationRepository(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(): ReactiveClientRegistrationRepository {
return InMemoryReactiveClientRegistrationRepository(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()
}
}
注册 SecurityWebFilterChain @Bean
以下示例显示了如何使用@EnableWebFluxSecurity
注册SecurityWebFilterChain
@Bean
并通过serverHttpSecurity.oauth2Login()
启用 OAuth 2.0 登录
-
Java
-
Kotlin
@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginSecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(authorize -> authorize
.anyExchange().authenticated()
)
.oauth2Login(withDefaults());
return http.build();
}
}
@Configuration
@EnableWebFluxSecurity
class OAuth2LoginSecurityConfig {
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
oauth2Login { }
}
return http.build()
}
}
完全覆盖自动配置
以下示例显示了如何通过注册ReactiveClientRegistrationRepository
@Bean
和SecurityWebFilterChain
@Bean
来完全覆盖自动配置。
-
Java
-
Kotlin
@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(authorize -> authorize
.anyExchange().authenticated()
)
.oauth2Login(withDefaults());
return http.build();
}
@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryReactiveClientRegistrationRepository(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
@EnableWebFluxSecurity
class OAuth2LoginConfig {
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
oauth2Login { }
}
return http.build()
}
@Bean
fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {
return InMemoryReactiveClientRegistrationRepository(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
@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(authorize -> authorize
.anyExchange().authenticated()
)
.oauth2Login(withDefaults());
return http.build();
}
@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryReactiveClientRegistrationRepository(this.googleClientRegistration());
}
@Bean
public ReactiveOAuth2AuthorizedClientService authorizedClientService(
ReactiveClientRegistrationRepository clientRegistrationRepository) {
return new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
}
@Bean
public ServerOAuth2AuthorizedClientRepository authorizedClientRepository(
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
return new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
}
private ClientRegistration googleClientRegistration() {
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
.clientId("google-client-id")
.clientSecret("google-client-secret")
.build();
}
}
@Configuration
@EnableWebFluxSecurity
class OAuth2LoginConfig {
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
oauth2Login { }
}
return http.build()
}
@Bean
fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {
return InMemoryReactiveClientRegistrationRepository(googleClientRegistration())
}
@Bean
fun authorizedClientService(
clientRegistrationRepository: ReactiveClientRegistrationRepository
): ReactiveOAuth2AuthorizedClientService {
return InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository)
}
@Bean
fun authorizedClientRepository(
authorizedClientService: ReactiveOAuth2AuthorizedClientService
): ServerOAuth2AuthorizedClientRepository {
return AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService)
}
private fun googleClientRegistration(): ClientRegistration {
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
.clientId("google-client-id")
.clientSecret("google-client-secret")
.build()
}
}