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

核心配置

Spring Boot 2.x 示例

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

本节介绍如何使用 Google 作为身份验证提供程序来配置 OAuth 2.0 登录示例,并涵盖以下主题: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 客户端,其凭据由客户端 ID 和客户端密钥组成。spring-doc.cadn.net.cn

设置重定向 URI

重定向 URI 是最终用户的用户代理在通过 Google 身份验证并在“同意”页面上授予对 OAuth 客户端(在上一步中创建)的访问权限后,将重定向回应用程序中的路径。spring-doc.cadn.net.cn

在“Set a redirect URI”小节中,确保 Authorized redirect URI 字段设置为localhost:8080/login/oauth2/code/google.spring-doc.cadn.net.cn

默认重定向 URI 模板为{baseUrl}/login/oauth2/code/{registrationId}. 这registrationIdClientRegistration.spring-doc.cadn.net.cn

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

配置 application.yml

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

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

    spring:
      security:
        oauth2:
          client:
            registration:	(1)
              google:	(2)
                client-id: google-client-id
                client-secret: google-client-secret
    OAuth 客户端属性
    1 spring.security.oauth2.client.registration是 OAuth 客户端属性的基本属性前缀。
    2 基本属性前缀后面是ClientRegistration,例如 Google。
  2. client-idclient-secret属性替换为您之前创建的 OAuth 2.0 凭证。spring-doc.cadn.net.cn

启动应用程序

启动 Spring Boot 2.x 示例并转到localhost:8080. 然后,您将被重定向到默认的自动生成的登录页面,该页面显示 Google 的链接。spring-doc.cadn.net.cn

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

使用您的 Google 帐户凭据进行身份验证后,您会看到 Consent (同意) 屏幕。 Consent (同意) 屏幕要求您允许或拒绝对您之前创建的 OAuth 客户端的访问。 单击 Allow (允许) 以授权 OAuth 客户端访问您的电子邮件地址和基本配置文件信息。spring-doc.cadn.net.cn

此时,OAuth 客户端从 UserInfo 端点检索您的电子邮件地址和基本配置文件信息,并建立经过身份验证的会话。spring-doc.cadn.net.cn

Spring Boot 2.x 属性映射

下表概述了 Spring Boot 2.x OAuth 客户端属性到ClientRegistration属性的映射。spring-doc.cadn.net.cn

Spring Boot 2.x 版本 客户注册

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 Provider 的配置端点或 Authorization Server 的元数据端点的发现,通过指定spring.security.oauth2.client.provider.[providerId].issuer-uri财产。spring-doc.cadn.net.cn

CommonOAuth2Provider

CommonOAuth2Provider为许多知名提供商预定义了一组默认客户端属性:Google、GitHub、Facebook 和 Okta。spring-doc.cadn.net.cn

例如,authorization-uri,token-uriuser-info-uri不要经常更改提供商。 因此,提供默认值以减少所需的配置是有意义的。spring-doc.cadn.net.cn

如前所述,当我们配置 Google 客户端时,只有client-idclient-secretproperties 是必需的。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
客户端属性的自动默认功能在这里无缝工作,因为registrationId (google) 与GOOGLE enum(不区分大小写)CommonOAuth2Provider.

对于您可能希望指定不同registrationIdgoogle-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 2.x 提供了以下基本属性来配置自定义提供程序属性: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 base 属性 (spring.security.oauth2.client.provider.okta) 允许自定义配置协议终端节点位置。

覆盖 Spring Boot 2.x 自动配置

用于 OAuth 客户端支持的 Spring Boot 2.x 自动配置类是OAuth2ClientAutoConfiguration.spring-doc.cadn.net.cn

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

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

  • 注册一个SecurityFilterChain @Bean并启用 OAuth 2.0 登录httpSecurity.oauth2Login().spring-doc.cadn.net.cn

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

注册 ClientRegistrationRepository @Bean

以下示例演示如何注册ClientRegistrationRepository @Bean:spring-doc.cadn.net.cn

@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并启用 OAuth 2.0 登录httpSecurity.oauth2Login():spring-doc.cadn.net.cn

OAuth2 登录配置
@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.spring-doc.cadn.net.cn

覆盖自动配置
@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 {
            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://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 2.x 的 Java 配置

如果您无法使用 Spring Boot 2.x,并且希望在CommonOAuth2Provider(例如,Google),请应用以下配置:spring-doc.cadn.net.cn

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