此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Security 7.0.4spring-doc.cadn.net.cn

核心配置

Spring Boot 示例

Spring Boot 为 OAuth 2.0 登录提供了完整的自动配置功能。spring-doc.cadn.net.cn

此部分展示了如何通过使用Google作为认证提供商(Authentication Provider)来配置OAuth 2.0 登录 WebFlux 样例,并涵盖了以下主题:spring-doc.cadn.net.cn

初始设置

要使用 Google 的 OAuth 2.0 身份验证系统进行登录,您必须在 Google API 控制台中设置一个项目以获取 OAuth 2.0 凭据。spring-doc.cadn.net.cn

请按照OpenID Connect页面上的说明进行操作,从“设置 OAuth 2.0”部分开始。spring-doc.cadn.net.cn

完成“获取 OAuth 2.0 凭证”说明后,你应该会有一个新的 OAuth 客户端,并且该客户端由客户端标识符(Client ID)和客户端密钥(Client Secret)组成。spring-doc.cadn.net.cn

设置重定向 URI

授权重定向URI是指在用户通过Google进行身份验证并被授予访问OAuth客户端(如上一步所述创建的)权限后,用户的用户代理将被重定向回应用中的路径。spring-doc.cadn.net.cn

在“设置重定向URI”子部分中,请确保授权的重定向URI字段被设置为localhost:8080/login/oauth2/code/googlespring-doc.cadn.net.cn

默认重定向URI模板是{baseUrl}/login/oauth2/code/{registrationId}。 The registrationIdClientRegistration 的唯一标识符。对于我们的示例,registrationIdgooglespring-doc.cadn.net.cn

如果 OAuth 客户端运行在代理服务器后面,建议检查 代理服务器配置,以确保应用程序已正确配置。 此外,请参阅 redirect-uri 支持的 URI 模板变量spring-doc.cadn.net.cn

配置application.yml

现在你已经创建了一个新的 Google OAuth 客户端,需要配置应用程序使用该 OAuth 客户端进行身份验证流程。 为此,请执行以下操作:spring-doc.cadn.net.cn

  1. 转到 application.yml 并设置以下配置:spring-doc.cadn.net.cn

    例 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。
  2. client-idclient-secret 属性中的值替换为你之前创建的 OAuth 2.0 凭据。spring-doc.cadn.net.cn

启动应用程序

启动 Spring Boot 示例应用,并访问 localhost:8080。 随后,您将被重定向到默认的自动生成登录页面,该页面会显示一个 Google 登录链接。spring-doc.cadn.net.cn

点击 Google 链接,随后将被重定向到 Google 进行身份验证。spring-doc.cadn.net.cn

使用您的 Google 账户凭据进行身份验证后,您将被引导到授权页面。 授权页面会要求您允许或拒绝授权之前创建的 OAuth 客户端访问权限。 请点击 允许 以授权 OAuth 客户端访问您的电子邮件地址和基本资料信息。spring-doc.cadn.net.cn

此时,OAuth 客户端会从用户信息端点(UserInfo Endpoint)获取您的电子邮件地址和基本个人资料信息,并建立一个已认证的会话。spring-doc.cadn.net.cn

Spring Boot 属性映射

下表列出了 Spring Boot OAuth 客户端属性与 ClientRegistration 属性之间的映射关系。spring-doc.cadn.net.cn

Spring Boot 客户端注册

spring.security.oauth2.client.registration.[registrationId]spring-doc.cadn.net.cn

registrationIdspring-doc.cadn.net.cn

spring.security.oauth2.client.registration.[registrationId].client-idspring-doc.cadn.net.cn

clientIdspring-doc.cadn.net.cn

spring.security.oauth2.client.registration.[registrationId].client-secretspring-doc.cadn.net.cn

clientSecretspring-doc.cadn.net.cn

spring.security.oauth2.client.registration.[registrationId].client-authentication-methodspring-doc.cadn.net.cn

clientAuthenticationMethodspring-doc.cadn.net.cn

spring.security.oauth2.client.registration.[registrationId].authorization-grant-typespring-doc.cadn.net.cn

authorizationGrantTypespring-doc.cadn.net.cn

spring.security.oauth2.client.registration.[registrationId].redirect-urispring-doc.cadn.net.cn

redirectUrispring-doc.cadn.net.cn

spring.security.oauth2.client.registration.[registrationId].scopespring-doc.cadn.net.cn

scopesspring-doc.cadn.net.cn

spring.security.oauth2.client.registration.[registrationId].client-namespring-doc.cadn.net.cn

clientNamespring-doc.cadn.net.cn

spring.security.oauth2.client.provider.[providerId].authorization-urispring-doc.cadn.net.cn

providerDetails.authorizationUrispring-doc.cadn.net.cn

spring.security.oauth2.client.provider.[providerId].token-urispring-doc.cadn.net.cn

providerDetails.tokenUrispring-doc.cadn.net.cn

spring.security.oauth2.client.provider.[providerId].jwk-set-urispring-doc.cadn.net.cn

providerDetails.jwkSetUrispring-doc.cadn.net.cn

spring.security.oauth2.client.provider.[providerId].issuer-urispring-doc.cadn.net.cn

providerDetails.issuerUrispring-doc.cadn.net.cn

spring.security.oauth2.client.provider.[providerId].user-info-urispring-doc.cadn.net.cn

providerDetails.userInfoEndpoint.urispring-doc.cadn.net.cn

spring.security.oauth2.client.provider.[providerId].user-info-authentication-methodspring-doc.cadn.net.cn

providerDetails.userInfoEndpoint.authenticationMethodspring-doc.cadn.net.cn

spring.security.oauth2.client.provider.[providerId].user-name-attributespring-doc.cadn.net.cn

providerDetails.userInfoEndpoint.userNameAttributeNamespring-doc.cadn.net.cn

一个ClientRegistration可以通过发现OpenID Connect提供者的配置端点或授权服务器的元数据端点来初始配置,通过指定spring.security.oauth2.client.provider.[providerId].issuer-uri属性。

通用 OAuth2 提供商

CommonOAuth2Provider 为多个知名提供商(包括 Google、GitHub、Facebook、X 和 Okta)预定义了一组默认的客户端属性。spring-doc.cadn.net.cn

例如,authorization-uritoken-uriuser-info-uri 对于提供者来说并不经常改变。 因此,提供默认值以减少所需的配置是有意义的。spring-doc.cadn.net.cn

如前所示,当我们配置 Google 客户端时,仅需提供 client-idclient-secret 属性。spring-doc.cadn.net.cn

以下列表展示了一个示例:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

以下列表展示了一个示例:spring-doc.cadn.net.cn

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 提供商支持多租户,这会导致每个租户(或子域)拥有不同的协议端点。spring-doc.cadn.net.cn

例如,注册到 Okta 的 OAuth 客户端会被分配到特定的子域,并拥有自己的协议端点。spring-doc.cadn.net.cn

针对这些情况,Spring Boot 提供了以下基础属性用于配置自定义提供者属性:spring.security.oauth2.client.provider.[providerId]spring-doc.cadn.net.cn

以下列表展示了一个示例:spring-doc.cadn.net.cn

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 客户端的自动配置类是ReactiveOAuth2ClientAutoConfigurationspring-doc.cadn.net.cn

它执行以下任务:spring-doc.cadn.net.cn

  • 注册一个ReactiveClientRegistrationRepository@Bean,该bean由配置的OAuth客户端属性中的ClientRegistration(s)组成。spring-doc.cadn.net.cn

  • 注册一个 SecurityWebFilterChain @Bean,并通过 serverHttpSecurity.oauth2Login() 启用 OAuth 2.0 登录。spring-doc.cadn.net.cn

如果你需要根据特定需求覆盖自动配置,可以通过以下方式进行:spring-doc.cadn.net.cn

注册一个 ReactiveClientRegistrationRepository @Bean

以下示例展示了如何注册一个 ReactiveClientRegistrationRepository @Beanspring-doc.cadn.net.cn

@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

以下示例展示了如何使用 SecurityWebFilterChain 注册一个 @Bean @EnableWebFluxSecurity,并通过 serverHttpSecurity.oauth2Login() 启用 OAuth 2.0 登录:spring-doc.cadn.net.cn

OAuth2 登录配置
@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 来完全覆盖自动配置。spring-doc.cadn.net.cn

覆盖自动配置
@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),请应用以下配置:spring-doc.cadn.net.cn

OAuth2 登录配置
@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()
    }
}