How-to: Authenticate using Social Login

本指南展示了如何为https://docs.spring.io/spring-security/reference/servlet/authentication/index.html[认证]配置Spring Authorization Server以及社会化登录提供程序(例如Google、GitHub等)。本指南的目的是演示如何用https://docs.spring.io/spring-security/reference/servlet/oauth2/login/index.html[OAuth 2.0 Login]替换https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/form.html[Form Login]。

Spring Authorization Server 建立在 Spring Security 中,并且我们将在本指南中使用 Spring Security 概念。

Register with Social Login Provider

首先,您需要使用选定的社交登录提供程序设置一个应用程序。常见的提供程序包括:

按照提供程序的步骤进行操作,直到被要求指定重定向 URI。要设置重定向 URI,请选择一个 registrationId(例如 googlemy-client 或您希望使用的任何其他唯一标识符),您将使用此标识符同时配置 Spring Security 提供程序。

registrationId`是 Spring Security 中`ClientRegistration`的唯一标识符。默认的重定向 URI 模板是{baseUrl}/login/oauth2/code/{registrationId}`。请参阅 设置重定向 URI,了解 Spring Security 参考部分中的更多信息。

例如,在本地端口 9000 上使用 registrationId google 进行测试时,您的重定向 URI 为 http://localhost:9000/login/oauth2/code/google。在使用您的提供商设置应用程序时,将此值输入为重定向 URI。

在完成社交登录提供程序的设置流程后,您应获取凭据(客户端 ID 和客户端密钥)。此外,您需要参考提供程序的文档并记下以下值:

  • Authorization URI: 提供商用来启动`authorization_code`流程的端点。

  • Token URI: 用于将`authorization_code`交换为`access_token`,还可以将 id_token

  • JWK Set URI: 用于获取用于验证 JWT 签名的密钥的端点,当`id_token`可用时需要该密钥。

  • User Info URI: 用于获取用户信息的端点,当`id_token`不可用时需要该信息。

  • User Name Attribute: `id_token`或用户信息响应中包含用户名称的声明。

Configure OAuth 2.0 Login

一旦您使用社交登录供应商执行了 registered,便可以继续为 OAuth 2.0 登录 配置 Spring Security。

Add OAuth2 Client Dependency

首先,添加以下依赖关系:

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"

Register a Client

接下来,使用 earlier 获取的值配置 ClientRegistration。以 Okta 为例,配置以下属性:

application.yml
okta:
  base-url: ${OKTA_BASE_URL}

spring:
  security:
    oauth2:
      client:
        registration:
          my-client:
            provider: okta
            client-id: ${OKTA_CLIENT_ID}
            client-secret: ${OKTA_CLIENT_SECRET}
            scope:
              - openid
              - profile
              - email
        provider:
          okta:
            authorization-uri: ${okta.base-url}/oauth2/v1/authorize
            token-uri: ${okta.base-url}/oauth2/v1/token
            user-info-uri: ${okta.base-url}/oauth2/v1/userinfo
            jwk-set-uri: ${okta.base-url}/oauth2/v1/keys
            user-name-attribute: sub

上述示例中的 registrationIdmy-client

上面的示例演示了使用环境变量(OKTA_BASE_URLOKTA_CLIENT_ID`和`OKTA_CLIENT_SECRET)设置提供商 URL、客户端 ID 和客户端机密的*recommended*方式。请参阅 外部化配置,了解 Spring Boot 参考部分中的更多信息。

此简单示例演示了典型配置,但某些提供商需要额外的配置。有关配置 ClientRegistration 的更多信息,请参阅 Spring Security 参考中的 Spring Boot 属性映射

Configure Authentication

最后,若要配置 Spring 授权服务器以将社交登录提供程序用于身份验证,你可以使用 oauth2Login() 而不是 formLogin()。你还可以通过使用 AuthenticationEntryPoint 配置 exceptionHandling() 将未经身份验证的用户自动重定向到该提供商。

继续执行 earlier example,使用 @Configuration 配置 Spring Security,如下例所示:

Configure OAuth 2.0 Login
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean (1)
	@Order(1)
	public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
			throws Exception {
		OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
		http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
			.oidc(Customizer.withDefaults());	// Enable OpenID Connect 1.0
		http
			// Redirect to the OAuth 2.0 Login endpoint when not authenticated
			// from the authorization endpoint
			.exceptionHandling((exceptions) -> exceptions
				.defaultAuthenticationEntryPointFor( (2)
					new LoginUrlAuthenticationEntryPoint("/oauth2/authorization/my-client"),
					new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
				)
			)
			// Accept access tokens for User Info and/or Client Registration
			.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));

		return http.build();
	}

	@Bean (3)
	@Order(2)
	public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
			throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			// OAuth2 Login handles the redirect to the OAuth 2.0 Login endpoint
			// from the authorization server filter chain
			.oauth2Login(Customizer.withDefaults()); (4)

		return http.build();
	}

}
<1>  用于 xref:../protocol-endpoints.adoc[Protocol Endpoints] 的 Spring Security 过滤链。
<1>  为重定向到 {spring-security-reference-base-url}/servlet/oauth2/login/advanced.html#oauth2login-advanced-login-page[OAuth 2.0 登录端点] 配置 `AuthenticationEntryPoint`。
<1>  link:https://docs.spring.io/spring-security/reference/servlet/authentication/index.html[authentication] 的 Spring Security 过滤器链。
<1>  为认证配置 {spring-security-reference-base-url}/servlet/oauth2/login/index.html[OAuth 2.0 登录] 。

如果您在执行 getting started 时配置了 UserDetailsService,现在便可将其移除。

Advanced Use Cases

demo authorization server sample 演示了联合身份提供者的高级配置选项。从以下用例中选择一个,查看每个用例的示例:

Capture Users in a Database

以下 AuthenticationSuccessHandler 示例在用户首次登录时使用自定义组件将用户捕获到本地数据库中:

FederatedIdentityAuthenticationSuccessHandler
Unresolved include directive in modules/ROOT/pages/guides/how-to-social-login.adoc - include::example$samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationSuccessHandler.java[]

使用上面的 AuthenticationSuccessHandler,你可以插入自己的 Consumer<OAuth2User>,它可以在数据库或其他数据存储中捕获用户以将联合账户链接或 JIT 账户授权等概念。这是一个简单的存储用户到内存中的示例:

UserRepositoryOAuth2UserHandler
Unresolved include directive in modules/ROOT/pages/guides/how-to-social-login.adoc - include::example$samples/demo-authorizationserver/src/main/java/sample/federation/UserRepositoryOAuth2UserHandler.java[]

Map Claims to an ID Token

以下 OAuth2TokenCustomizer 示例将用户来自身份验证提供程序的声明映射到 Spring 授权服务器生成的 id_token

FederatedIdentityIdTokenCustomizer
Unresolved include directive in modules/ROOT/pages/guides/how-to-social-login.adoc - include::example$samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityIdTokenCustomizer.java[]

你可以通过发布 @Bean 以 Spring Authorization Server 使用此自定义器,如下例所示:

Configure FederatedIdentityIdTokenCustomizer
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> idTokenCustomizer() {
    return new FederatedIdentityIdTokenCustomizer();
}