# 核心配置

# Spring 启动 2.x 示例

Spring Boot2.x 为 OAuth2.0 登录带来了完整的自动配置功能。

本节展示如何使用谷歌作为身份验证提供者配置**OAuth2.0 登录 WebFlux 样本 ** (opens new window),并涵盖以下主题:

# 初始设置

要使用 Google 的 OAuth2.0 身份验证系统进行登录,你必须在 Google API 控制台中设置一个项目,以获得 OAuth2.0 凭据。

用于身份验证的谷歌的 OAuth2.0 实现 (opens new window)符合OpenID Connect1.0 (opens new window)规范,并且是OpenID 认证 (opens new window)

按照OpenID 连接 (opens new window)页面上的说明,从“设置 OAuth2.0”一节开始。

在完成“获取 OAuth2.0 凭据”说明之后,你应该有一个新的 OAuth 客户机,其凭据由一个客户机 ID 和一个客户机秘密组成。

# 设置重定向 URI

重定向 URI 是应用程序中的路径,终端用户的用户代理在与 Google 进行身份验证并授予对同意页面上的 OAuth 客户端 在前一步中创建 的访问权限后,会被重定向回应用程序中的路径。

在“设置重定向 URI”子节中,确保将授权重定向 URI字段设置为[http://localhost:8080/login/oauth2/code/google](http://localhost:8080/login/oauth2/code/google)

默认的重定向 URI 模板是{baseUrl}/login/oauth2/code/{registrationId}
***注册 ID***是客户登记的唯一标识符。
对于我们的示例,registrationIdgoogle
如果 OAuth 客户端运行在代理服务器后面,建议检查代理服务器配置以确保应用程序配置正确。
此外,请参阅支持的[URI模板变量](../client/Authorization-grants.html#OAuth2client-auth-code-redirect-uri)。

# 配置application.yml

现在,你有了一个新的 OAuth 客户机和 Google,你需要将应用程序配置为使用认证流程的 OAuth 客户机。这样做:

  1. 转到application.yml并设置以下配置:

    spring:
      security:
        oauth2:
          client:
            registration:	(1)
              google:	(2)
                client-id: google-client-id
                client-secret: google-client-secret
    

    例 1。OAuth 客户属性

    1 spring.security.oauth2.client.registration是 OAuth 客户机属性的基本属性前缀。
    2 在基本属性前缀之后是[ClientRegistration](../client/core.html#OAuth2client-client-registration)的 ID,例如 Google。
  2. 用前面创建的 OAuth2.0 凭据替换client-idclient-secret属性中的值。

# 启动应用程序

启动 Spring boot2.x 示例,然后转到[http://localhost:8080](http://localhost:8080)。然后,你将被重定向到默认的自动生成登录页面,该页面显示了 Google 的链接。

点击谷歌链接,然后你将被重定向到谷歌进行身份验证。

在使用谷歌帐户凭据进行身份验证后,显示给你的下一页是“同意”屏幕。同意屏幕要求你允许或拒绝访问你之前创建的 OAuth 客户端。单击允许授权 OAuth 客户端访问你的电子邮件地址和基本配置文件信息。

此时,OAuth 客户机从用户信息端点 (opens new window)检索你的电子邮件地址和基本配置文件信息,并建立经过身份验证的会话。

# Spring 启动 2.x 属性映射

下表概述了 Spring Boot2.x OAuth 客户机属性到客户登记属性的映射。

Spring 启动 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
通过指定spring.security.oauth2.client.provider.*[providerId]*.issuer-uri属性,可以使用发现 OpenID Connect 提供者的配置端点 (opens new window)或授权服务器的元数据端点 (opens new window)来初始配置ClientRegistration

# CommonoAuth2Provider

CommonOAuth2Provider为许多著名的提供商预先定义了一组默认的客户端属性:Google、GitHub、Facebook 和 OKTA。

例如,对于提供者,authorization-uritoken-uriuser-info-uri不会经常更改。因此,为了减少所需的配置,提供默认值是有意义的。

如前所述,当我们配置了一个 Google 客户端时,只需要client-idclient-secret属性。

下面的清单展示了一个示例:

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: google-client-id
            client-secret: google-client-secret
客户机属性的自动默认在这里无缝地工作,因为registrationIdgoogle)匹配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()中设置的客户机属性的自动违约。

# 配置自定义提供程序属性

有一些 OAuth2.0 提供程序支持多租赁,这会导致每个租户(或子域)的协议端点不同。

例如,向 OKTA 注册的 OAuth 客户端被分配到特定的子域并具有自己的协议端点。

对于这些情况, Spring Boot2.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:	(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 启动 2.x 自动配置

OAuth 客户端支持的 Spring Boot2.x 自动配置类是ReactiveOAuth2ClientAutoConfiguration

它执行以下任务:

  • 从配置的 OAuth 客户机属性中寄存器ReactiveClientRegistrationRepository``@Bean组成的ClientRegistration

  • 注册SecurityWebFilterChain``@Bean并通过serverHttpSecurity.oauth2Login()启用 OAuth2.0 登录。

如果需要根据你的特定需求重写自动配置,可以通过以下方式执行:

# Register a ReactiveClientRegistrationRepository @Bean

下面的示例显示了如何注册ReactiveClientRegistrationRepository``@Bean:

爪哇

@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();
	}
}

Kotlin

@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()
    }
}

# Register a SecurityWebFilterChain @Bean

下面的示例显示了如何用@EnableWebFluxSecurity注册SecurityWebFilterChain``@Bean并通过serverHttpSecurity.oauth2Login()启用 OAuth2.0 登录:

例 2。OAuth2 登录配置

爪哇

@EnableWebFluxSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
		http
			.authorizeExchange(authorize -> authorize
				.anyExchange().authenticated()
			)
			.oauth2Login(withDefaults());

		return http.build();
	}
}

Kotlin

@EnableWebFluxSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        return http {
            authorizeExchange {
                authorize(anyExchange, authenticated)
            }
            oauth2Login { }
        }
    }
}

# 完全覆盖自动配置

下面的示例展示了如何通过注册一个ReactiveClientRegistrationRepository``@Bean和一个SecurityWebFilterChain``@Bean来完全覆盖自动配置。

例 3。覆盖自动配置

爪哇

@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();
	}
}

Kotlin

@EnableWebFluxSecurity
class OAuth2LoginConfig {

    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        return http {
            authorizeExchange {
                authorize(anyExchange, authenticated)
            }
            oauth2Login { }
        }
    }

    @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 boot2.x 的 爪哇 配置

如果你不能使用 Spring Boot2.x,并且希望在CommonOAuth2Provider中配置一个预定义的提供程序(例如,Google),请应用以下配置:

例 4。OAuth2 登录配置

Java

@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();
	}
}

Kotlin

@EnableWebFluxSecurity
class OAuth2LoginConfig {

    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        return http {
            authorizeExchange {
                authorize(anyExchange, authenticated)
            }
            oauth2Login { }
        }
    }

    @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()
    }
}

OAuth2 登录高级配置