# 核心配置

# Spring 启动2.x示例

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

本节展示如何使用谷歌作为身份验证提供者配置OAuth2.0登录示例 (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)

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

在完成“获取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***是客户登记的唯一标识符。
如果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 在基本属性前缀之后是客户登记的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自动配置类是OAuth2ClientAutoConfiguration

它执行以下任务:

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

  • 提供WebSecurityConfigurerAdapter``@Configuration并通过httpSecurity.oauth2Login()启用OAuth2.0登录。

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

# Register a ClientRegistrationRepository @Bean

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

Java

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

Kotlin

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

# 提供一个WebSecurityConfigureRadapter

下面的示例展示了如何使用@EnableWebSecurity提供WebSecurityConfigurerAdapter并通过httpSecurity.oauth2Login()启用OAuth2.0登录:

例2. OAuth2登录配置

Java

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login(withDefaults());
	}
}

Kotlin

@EnableWebSecurity
class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login { }
        }
    }
}

# 完全覆盖自动配置

下面的示例展示了如何通过注册一个ClientRegistrationRepository``@Bean并提供一个WebSecurityConfigurerAdapter来完全覆盖自动配置。

例3.覆盖自动配置

Java

@Configuration
public class OAuth2LoginConfig {

	@EnableWebSecurity
	public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

		@Override
		protected void configure(HttpSecurity http) throws Exception {
			http
				.authorizeHttpRequests(authorize -> authorize
					.anyRequest().authenticated()
				)
				.oauth2Login(withDefaults());
		}
	}

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

Kotlin

@Configuration
class OAuth2LoginConfig {

    @EnableWebSecurity
    class OAuth2LoginSecurityConfig: WebSecurityConfigurerAdapter() {

        override fun configure(http: HttpSecurity) {
            http {
                authorizeRequests {
                    authorize(anyRequest, authenticated)
                }
                oauth2Login { }
            }
        }
    }

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

# Java 配置不需要 Spring 启动2.x

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

例4. OAuth2登录配置

Java

@Configuration
public class OAuth2LoginConfig {

	@EnableWebSecurity
	public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

		@Override
		protected void configure(HttpSecurity http) throws Exception {
			http
				.authorizeHttpRequests(authorize -> authorize
					.anyRequest().authenticated()
				)
				.oauth2Login(withDefaults());
		}
	}

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

Kotlin

@Configuration
open class OAuth2LoginConfig {
    @EnableWebSecurity
    open class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
        override fun configure(http: HttpSecurity) {
            http {
                authorizeRequests {
                    authorize(anyRequest, authenticated)
                }
                oauth2Login { }
            }
        }
    }

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

XML

<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>

OAuth2登录高级配置