Advanced Configuration

HttpSecurity.oauth2Login() 提供了多个用于自定义 OAuth 2.0 Login 的配置选项。主要配置选项统筹到其协议端点对等体。

HttpSecurity.oauth2Login() provides a number of configuration options for customizing OAuth 2.0 Login. The main configuration options are grouped into their protocol endpoint counterparts.

例如,oauth2Login().authorizationEndpoint() 允许配置 Authorization Endpoint,而 oauth2Login().tokenEndpoint() 允许配置 Token Endpoint

For example, oauth2Login().authorizationEndpoint() allows configuring the Authorization Endpoint, whereas oauth2Login().tokenEndpoint() allows configuring the Token Endpoint.

以下代码显示了一个示例:

The following code shows an example:

Advanced OAuth2 Login Configuration
  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
			    .authorizationEndpoint(authorization -> authorization
			            ...
			    )
			    .redirectionEndpoint(redirection -> redirection
			            ...
			    )
			    .tokenEndpoint(token -> token
			            ...
			    )
			    .userInfoEndpoint(userInfo -> userInfo
			            ...
			    )
			);
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                authorizationEndpoint {
                    ...
                }
                redirectionEndpoint {
                    ...
                }
                tokenEndpoint {
                    ...
                }
                userInfoEndpoint {
                    ...
                }
            }
        }
        return http.build()
    }
}

oauth2Login() DSL 的主要目标是与规范中定义的命名紧密对齐。

The main goal of the oauth2Login() DSL was to closely align with the naming, as defined in the specifications.

OAuth 2.0 授权框架将 Protocol Endpoints 定义如下:

The OAuth 2.0 Authorization Framework defines the Protocol Endpoints as follows:

授权过程使用两个授权服务器端点(HTTP 资源):

The authorization process uses two authorization server endpoints (HTTP resources):

  • Authorization Endpoint: Used by the client to obtain authorization from the resource owner through user-agent redirection.

  • Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication.

授权过程还使用一个客户端端点:

The authorization process also uses one client endpoint:

  • Redirection Endpoint: Used by the authorization server to return responses that contain authorization credentials to the client through the resource owner user-agent.

OpenID Connect 核心 1.0 规范将 UserInfo Endpoint 定义如下:

The OpenID Connect Core 1.0 specification defines the UserInfo Endpoint as follows:

UserInfo 端点是一个 OAuth 2.0 受保护资源,返回有关经过身份验证的最终用户的信息。为了获得有关最终用户请求的信息,客户端使用通过 OpenID Connect Authentication 获得的访问令牌,向 UserInfo 端点发出请求。这些信息通常由一个 JSON 对象表示,该对象包含一系列用于信息的名称-值对。

The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user. To obtain the requested claims about the end-user, the client makes a request to the UserInfo Endpoint by using an access token obtained through OpenID Connect Authentication. These claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims.

以下代码显示了 oauth2Login() DSL 可用的完整配置选项:

The following code shows the complete configuration options available for the oauth2Login() DSL:

OAuth2 Login Configuration Options
  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
			    .clientRegistrationRepository(this.clientRegistrationRepository())
			    .authorizedClientRepository(this.authorizedClientRepository())
			    .authorizedClientService(this.authorizedClientService())
			    .loginPage("/login")
			    .authorizationEndpoint(authorization -> authorization
			        .baseUri(this.authorizationRequestBaseUri())
			        .authorizationRequestRepository(this.authorizationRequestRepository())
			        .authorizationRequestResolver(this.authorizationRequestResolver())
			    )
			    .redirectionEndpoint(redirection -> redirection
			        .baseUri(this.authorizationResponseBaseUri())
			    )
			    .tokenEndpoint(token -> token
			        .accessTokenResponseClient(this.accessTokenResponseClient())
			    )
			    .userInfoEndpoint(userInfo -> userInfo
			        .userAuthoritiesMapper(this.userAuthoritiesMapper())
			        .userService(this.oauth2UserService())
			        .oidcUserService(this.oidcUserService())
			    )
			);
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                clientRegistrationRepository = clientRegistrationRepository()
                authorizedClientRepository = authorizedClientRepository()
                authorizedClientService = authorizedClientService()
                loginPage = "/login"
                authorizationEndpoint {
                    baseUri = authorizationRequestBaseUri()
                    authorizationRequestRepository = authorizationRequestRepository()
                    authorizationRequestResolver = authorizationRequestResolver()
                }
                redirectionEndpoint {
                    baseUri = authorizationResponseBaseUri()
                }
                tokenEndpoint {
                    accessTokenResponseClient = accessTokenResponseClient()
                }
                userInfoEndpoint {
                    userAuthoritiesMapper = userAuthoritiesMapper()
                    userService = oauth2UserService()
                    oidcUserService = oidcUserService()
                }
            }
        }
        return http.build()
    }
}

除了 oauth2Login() DSL,还支持 XML 配置。

In addition to the oauth2Login() DSL, XML configuration is also supported.

以下代码显示了 security namespace 中可用的完整配置选项:

The following code shows the complete configuration options available in the security namespace:

OAuth2 Login XML Configuration Options
<http>
	<oauth2-login client-registration-repository-ref="clientRegistrationRepository"
				  authorized-client-repository-ref="authorizedClientRepository"
				  authorized-client-service-ref="authorizedClientService"
				  authorization-request-repository-ref="authorizationRequestRepository"
				  authorization-request-resolver-ref="authorizationRequestResolver"
				  access-token-response-client-ref="accessTokenResponseClient"
				  user-authorities-mapper-ref="userAuthoritiesMapper"
				  user-service-ref="oauth2UserService"
				  oidc-user-service-ref="oidcUserService"
				  login-processing-url="/login/oauth2/code/*"
				  login-page="/login"
				  authentication-success-handler-ref="authenticationSuccessHandler"
				  authentication-failure-handler-ref="authenticationFailureHandler"
				  jwt-decoder-factory-ref="jwtDecoderFactory"/>
</http>

以下各节将详细介绍每个可用的配置选项:

The following sections go into more detail on each of the configuration options available:

OAuth 2.0 Login Page

默认情况下,OAuth 2.0 登录页面由 DefaultLoginPageGeneratingFilter 自动生成。默认登录页面显示每个已配置 OAuth 客户端及其 ClientRegistration.clientName 作为一个链接,它能够启动授权请求(或 OAuth 2.0 登录)。

By default, the OAuth 2.0 Login Page is auto-generated by the DefaultLoginPageGeneratingFilter. The default login page shows each configured OAuth Client with its ClientRegistration.clientName as a link, which is capable of initiating the Authorization Request (or OAuth 2.0 Login).

要使 DefaultLoginPageGeneratingFilter 显示已配置 OAuth 客户端的链接,已注册的 ClientRegistrationRepository 还需要实现 Iterable<ClientRegistration>。请参阅 InMemoryClientRegistrationRepository 以获取参考。

For DefaultLoginPageGeneratingFilter to show links for configured OAuth Clients, the registered ClientRegistrationRepository needs to also implement Iterable<ClientRegistration>. See InMemoryClientRegistrationRepository for reference.

每个 OAuth 客户端的链接目标默认为以下内容:

The link’s destination for each OAuth Client defaults to the following:

OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{registrationId}"

以下行为示例:

The following line shows an example:

<a href="/oauth2/authorization/google">Google</a>

若要覆盖默认登录页面,请配置 oauth2Login().loginPage() 和(可选)oauth2Login().authorizationEndpoint().baseUri()

To override the default login page, configure oauth2Login().loginPage() and (optionally) oauth2Login().authorizationEndpoint().baseUri().

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

The following listing shows an example:

OAuth2 Login Page Configuration
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
			    .loginPage("/login/oauth2")
			    ...
			    .authorizationEndpoint(authorization -> authorization
			        .baseUri("/login/oauth2/authorization")
			        ...
			    )
			);
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                loginPage = "/login/oauth2"
                authorizationEndpoint {
                    baseUri = "/login/oauth2/authorization"
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-login login-page="/login/oauth2"
				  ...
    />
</http>

您需要提供一个 @Controller 及其 @RequestMapping("/login/oauth2"),能够呈现自定义登录页面。

You need to provide a @Controller with a @RequestMapping("/login/oauth2") that is capable of rendering the custom login page.

如前所述,配置 oauth2Login().authorizationEndpoint().baseUri() 是可选的。但是,如果您选择自定义它,请确保到每个 OAuth 客户端的链接都与 authorizationEndpoint().baseUri() 相匹配。

As noted earlier, configuring oauth2Login().authorizationEndpoint().baseUri() is optional. However, if you choose to customize it, ensure the link to each OAuth Client matches the authorizationEndpoint().baseUri().

以下行为示例:

The following line shows an example:

<a href="/login/oauth2/authorization/google">Google</a>

Redirection Endpoint

授权服务器使用重定向端点来通过资源所有者的用户代理将授权响应(其中包含授权凭证)返回到客户端。

The Redirection Endpoint is used by the Authorization Server for returning the Authorization Response (which contains the authorization credentials) to the client through the Resource Owner user-agent.

OAuth 2.0 登录利用授权代码授予。因此,授权凭证就是授权代码。

OAuth 2.0 Login leverages the Authorization Code Grant. Therefore, the authorization credential is the authorization code.

默认授权响应 baseUri(重定向端点)是 /login/oauth2/code/*,在 OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI 中定义。

The default Authorization Response baseUri (redirection endpoint) is /login/oauth2/code/*, which is defined in OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI.

如果您要自定义授权响应 baseUri,请按以下方式进行配置:

If you would like to customize the Authorization Response baseUri, configure it as follows:

Redirection Endpoint Configuration
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

    @Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
			    .redirectionEndpoint(redirection -> redirection
			        .baseUri("/login/oauth2/callback/*")
			        ...
			    )
			);
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                redirectionEndpoint {
                    baseUri = "/login/oauth2/callback/*"
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-login login-processing-url="/login/oauth2/callback/*"
				  ...
    />
</http>

您还需要确保 ClientRegistration.redirectUri 与自定义授权响应 baseUri 相匹配。

You also need to ensure the ClientRegistration.redirectUri matches the custom Authorization Response baseUri.

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

The following listing shows an example:

Java
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
	.clientId("google-client-id")
	.clientSecret("google-client-secret")
	.redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
	.build();
Kotlin
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
    .clientId("google-client-id")
    .clientSecret("google-client-secret")
    .redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
    .build()

UserInfo Endpoint

UserInfo 终结点包括多个配置选项,如下小节所述:

The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections:

Mapping User Authorities

在用户成功通过 OAuth 2.0 提供程序进行身份验证之后,OAuth2User.getAuthorities()(或 OidcUser.getAuthorities())将包含一个权限列表,该列表从 OAuth2UserRequest.getAccessToken().getScopes() 中填充并以 SCOPE_ 作为前缀。这些被授予的权限可以映射到一组新的 GrantedAuthority 实例,在完成身份验证时提供给 OAuth2AuthenticationToken

After the user successfully authenticates with the OAuth 2.0 Provider, the OAuth2User.getAuthorities() (or OidcUser.getAuthorities()) contains a list of granted authorities populated from OAuth2UserRequest.getAccessToken().getScopes() and prefixed with SCOPE_. These granted authorities can be mapped to a new set of GrantedAuthority instances, which are supplied to OAuth2AuthenticationToken when completing the authentication.

OAuth2AuthenticationToken.getAuthorities() 用于授权请求,例如 hasRole('USER')hasRole('ADMIN') 中的请求。

OAuth2AuthenticationToken.getAuthorities() is used for authorizing requests, such as in hasRole('USER') or hasRole('ADMIN').

在映射用户权限时有几个选项可供选择:

There are a couple of options to choose from when mapping user authorities:

Using a GrantedAuthoritiesMapper

GrantedAuthoritiesMapper 具有被授予的权限列表,其中包含 OAuth2UserAuthority 类型和权限字符串 OAUTH2_USER(或 OidcUserAuthority 和权限字符串 OIDC_USER)的特殊权限。

The GrantedAuthoritiesMapper is given a list of granted authorities which contains a special authority of type OAuth2UserAuthority and the authority string OAUTH2_USER (or OidcUserAuthority and the authority string OIDC_USER).

提供 GrantedAuthoritiesMapper 的实现并进行以下配置:

Provide an implementation of GrantedAuthoritiesMapper and configure it, as follows:

Granted Authorities Mapper Configuration
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

    @Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
			    .userInfoEndpoint(userInfo -> userInfo
			        .userAuthoritiesMapper(this.userAuthoritiesMapper())
			        ...
			    )
			);
		return http.build();
	}

	private GrantedAuthoritiesMapper userAuthoritiesMapper() {
		return (authorities) -> {
			Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

			authorities.forEach(authority -> {
				if (OidcUserAuthority.class.isInstance(authority)) {
					OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;

					OidcIdToken idToken = oidcUserAuthority.getIdToken();
					OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();

					// Map the claims found in idToken and/or userInfo
					// to one or more GrantedAuthority's and add it to mappedAuthorities

				} else if (OAuth2UserAuthority.class.isInstance(authority)) {
					OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;

					Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();

					// Map the attributes found in userAttributes
					// to one or more GrantedAuthority's and add it to mappedAuthorities

				}
			});

			return mappedAuthorities;
		};
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                userInfoEndpoint {
                    userAuthoritiesMapper = userAuthoritiesMapper()
                }
            }
        }
        return http.build()
    }

    private fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection<GrantedAuthority> ->
        val mappedAuthorities = emptySet<GrantedAuthority>()

        authorities.forEach { authority ->
            if (authority is OidcUserAuthority) {
                val idToken = authority.idToken
                val userInfo = authority.userInfo
                // Map the claims found in idToken and/or userInfo
                // to one or more GrantedAuthority's and add it to mappedAuthorities
            } else if (authority is OAuth2UserAuthority) {
                val userAttributes = authority.attributes
                // Map the attributes found in userAttributes
                // to one or more GrantedAuthority's and add it to mappedAuthorities
            }
        }

        mappedAuthorities
    }
}
<http>
	<oauth2-login user-authorities-mapper-ref="userAuthoritiesMapper"
				  ...
    />
</http>

或者,您可以注册一个 GrantedAuthoritiesMapper @Bean,以自动将其应用到以下配置:

Alternatively, you can register a GrantedAuthoritiesMapper @Bean to have it automatically applied to the configuration, as follows:

Granted Authorities Mapper Bean Configuration
  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
		    .oauth2Login(withDefaults());
		return http.build();
	}

	@Bean
	public GrantedAuthoritiesMapper userAuthoritiesMapper() {
		...
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login { }
        }
        return http.build()
    }

    @Bean
    fun userAuthoritiesMapper(): GrantedAuthoritiesMapper {
        ...
    }
}

Delegation-based Strategy with OAuth2UserService

与使用 GrantedAuthoritiesMapper 相比,此策略是高级的。然而,它也更灵活,因为它可以访问 OAuth2UserRequestOAuth2User(在使用 OAuth 2.0 UserService 时)或 OidcUserRequestOidcUser(在使用 OpenID Connect 1.0 UserService 时)。

This strategy is advanced compared to using a GrantedAuthoritiesMapper. However, it is also more flexible, as it gives you access to the OAuth2UserRequest and OAuth2User (when using an OAuth 2.0 UserService) or OidcUserRequest and OidcUser (when using an OpenID Connect 1.0 UserService).

OAuth2UserRequest(和 OidcUserRequest)可让您访问关联的 OAuth2AccessToken,在 delegator 需要从受保护的资源中获取权限信息才能映射用户的自定义权限的情况下,这是一个非常有用的功能。

The OAuth2UserRequest (and OidcUserRequest) provides you access to the associated OAuth2AccessToken, which is very useful in cases where the delegator needs to fetch authority information from a protected resource before it can map the custom authorities for the user.

以下示例展示了如何使用 OpenID Connect 1.0 UserService 实现和配置基于委派的策略:

The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:

OAuth2UserService Configuration
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
			    .userInfoEndpoint(userInfo -> userInfo
			        .oidcUserService(this.oidcUserService())
			        ...
			    )
			);
		return http.build();
	}

	private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
		final OidcUserService delegate = new OidcUserService();

		return (userRequest) -> {
			// Delegate to the default implementation for loading a user
			OidcUser oidcUser = delegate.loadUser(userRequest);

			OAuth2AccessToken accessToken = userRequest.getAccessToken();
			Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

			// TODO
			// 1) Fetch the authority information from the protected resource using accessToken
			// 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities

			// 3) Create a copy of oidcUser but use the mappedAuthorities instead
			ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
			String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();
			if (StringUtils.hasText(userNameAttributeName)) {
				oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo(), userNameAttributeName);
			} else {
				oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
			}

			return oidcUser;
		};
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig  {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                userInfoEndpoint {
                    oidcUserService = oidcUserService()
                }
            }
        }
        return http.build()
    }

    @Bean
    fun oidcUserService(): OAuth2UserService<OidcUserRequest, OidcUser> {
        val delegate = OidcUserService()

        return OAuth2UserService { userRequest ->
            // Delegate to the default implementation for loading a user
            val oidcUser = delegate.loadUser(userRequest)

            val accessToken = userRequest.accessToken
            val mappedAuthorities = HashSet<GrantedAuthority>()

            // TODO
            // 1) Fetch the authority information from the protected resource using accessToken
            // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities
            // 3) Create a copy of oidcUser but use the mappedAuthorities instead
            val providerDetails = userRequest.getClientRegistration().getProviderDetails()
            val userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName()
            if (StringUtils.hasText(userNameAttributeName)) {
                DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo, userNameAttributeName)
            } else {
                DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo)
            }
        }
    }
}
<http>
	<oauth2-login oidc-user-service-ref="oidcUserService"
				  ...
    />
</http>

OAuth 2.0 UserService

DefaultOAuth2UserServiceOAuth2UserService 的一个实现,它支持标准 OAuth 2.0 提供商。

DefaultOAuth2UserService is an implementation of an OAuth2UserService that supports standard OAuth 2.0 Provider’s.

OAuth2UserService 从 UserInfo 端点(通过使用在授权流程期间授予客户端的访问令牌)获取最终用户(资源所有者)的用户属性,并以 OAuth2User 的形式返回 AuthenticatedPrincipal

OAuth2UserService obtains the user attributes of the end-user (the resource owner) from the UserInfo Endpoint (by using the access token granted to the client during the authorization flow) and returns an AuthenticatedPrincipal in the form of an OAuth2User.

DefaultOAuth2UserService 在 UserInfo 端点请求用户属性时,使用 RestOperations 实例。

DefaultOAuth2UserService uses a RestOperations instance when requesting the user attributes at the UserInfo Endpoint.

如果你需要自定义 UserInfo Request 的预处理,则可以为 DefaultOAuth2UserService.setRequestEntityConverter() 提供自定义 Converter<OAuth2UserRequest, RequestEntity<?>>。默认实现 OAuth2UserRequestEntityConverter 构建了一个 UserInfo Request 的 RequestEntity 表示形式,默认情况下,在 Authorization 标头中设置 OAuth2AccessToken

If you need to customize the pre-processing of the UserInfo Request, you can provide DefaultOAuth2UserService.setRequestEntityConverter() with a custom Converter<OAuth2UserRequest, RequestEntity<?>>. The default implementation OAuth2UserRequestEntityConverter builds a RequestEntity representation of a UserInfo Request that sets the OAuth2AccessToken in the Authorization header by default.

另一方面,如果你需要自定义 UserInfo Response 的后处理,则需要为 DefaultOAuth2UserService.setRestOperations() 提供自定义配置的 RestOperations。默认 RestOperations 配置如下:

On the other end, if you need to customize the post-handling of the UserInfo Response, you need to provide DefaultOAuth2UserService.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

OAuth2ErrorResponseErrorHandlerResponseErrorHandler,它可以处理 OAuth 2.0 错误(400 Bad Request)。它使用 OAuth2ErrorHttpMessageConverter 将 OAuth 2.0 错误参数转换为 OAuth2Error

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error (400 Bad Request). It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error.

无论你自定义 DefaultOAuth2UserService 还是提供自己的 OAuth2UserService 实现,你都需要按照如下方式配置它:

Whether you customize DefaultOAuth2UserService or provide your own implementation of OAuth2UserService, you need to configure it as follows:

  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
			    .userInfoEndpoint(userInfo -> userInfo
			        .userService(this.oauth2UserService())
			        ...
			    )
			);
		return http.build();
	}

	private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
		...
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                userInfoEndpoint {
                    userService = oauth2UserService()
                    // ...
                }
            }
        }
        return http.build()
    }

    private fun oauth2UserService(): OAuth2UserService<OAuth2UserRequest, OAuth2User> {
        // ...
    }
}

OpenID Connect 1.0 UserService

OidcUserServiceOAuth2UserService 的一个实现,它支持 OpenID Connect 1.0 提供商。

OidcUserService is an implementation of an OAuth2UserService that supports OpenID Connect 1.0 Provider’s.

OidcUserService 在 UserInfo 端点请求用户属性时利用 DefaultOAuth2UserService

The OidcUserService leverages the DefaultOAuth2UserService when requesting the user attributes at the UserInfo Endpoint.

如果你需要自定义 UserInfo Request 的预处理或 UserInfo Response 的后处理,则需要为 OidcUserService.setOauth2UserService() 提供自定义配置的 DefaultOAuth2UserService

If you need to customize the pre-processing of the UserInfo Request or the post-handling of the UserInfo Response, you need to provide OidcUserService.setOauth2UserService() with a custom configured DefaultOAuth2UserService.

无论你自定义 OidcUserService 还是为 OpenID Connect 1.0 提供商提供 OAuth2UserService 自己的实现,你都需要按照如下方式配置它:

Whether you customize OidcUserService or provide your own implementation of OAuth2UserService for OpenID Connect 1.0 Provider’s, you need to configure it as follows:

  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Login(oauth2 -> oauth2
				.userInfoEndpoint(userInfo -> userInfo
				    .oidcUserService(this.oidcUserService())
				    ...
			    )
			);
		return http.build();
	}

	private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
		...
	}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Login {
                userInfoEndpoint {
                    oidcUserService = oidcUserService()
                    // ...
                }
            }
        }
        return http.build()
    }

    private fun oidcUserService(): OAuth2UserService<OidcUserRequest, OidcUser> {
        // ...
    }
}

ID Token Signature Verification

OpenID Connect 1.0 认证引入了 ID Token ,该 ID Token 是一个安全令牌,其中包含由授权服务器在客户端使用时对最终用户的认证索赔。

OpenID Connect 1.0 Authentication introduces the ID Token, which is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when used by a Client.

ID 令牌表示为 JSON Web Token (JWT),必须使用 JSON Web Signature (JWS) 进行签名。

The ID Token is represented as a JSON Web Token (JWT) and MUST be signed by using JSON Web Signature (JWS).

OidcIdTokenDecoderFactory 提供 JwtDecoder,用于 OidcIdToken 签名验证。默认算法为 RS256,但在客户端注册期间分配时可能有所不同。对于这些情况,你可以配置解析器以返回为特定客户端分配的预期 JWS 算法。

The OidcIdTokenDecoderFactory provides a JwtDecoder used for OidcIdToken signature verification. The default algorithm is RS256 but may be different when assigned during client registration. For these cases, you can configure a resolver to return the expected JWS algorithm assigned for a specific client.

JWS 算法解析器是 Function,它接受 ClientRegistration 并为客户端返回预期的 JwsAlgorithm,例如 SignatureAlgorithm.RS256MacAlgorithm.HS256

The JWS algorithm resolver is a Function that accepts a ClientRegistration and returns the expected JwsAlgorithm for the client, such as SignatureAlgorithm.RS256 or MacAlgorithm.HS256

以下代码展示了如何配置 OidcIdTokenDecoderFactory @Bean,使其对于所有 ClientRegistration 实例默认为 MacAlgorithm.HS256

The following code shows how to configure the OidcIdTokenDecoderFactory @Bean to default to MacAlgorithm.HS256 for all ClientRegistration instances:

  • Java

  • Kotlin

@Bean
public JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
	OidcIdTokenDecoderFactory idTokenDecoderFactory = new OidcIdTokenDecoderFactory();
	idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);
	return idTokenDecoderFactory;
}
@Bean
fun idTokenDecoderFactory(): JwtDecoderFactory<ClientRegistration?> {
    val idTokenDecoderFactory = OidcIdTokenDecoderFactory()
    idTokenDecoderFactory.setJwsAlgorithmResolver { MacAlgorithm.HS256 }
    return idTokenDecoderFactory
}

对于基于 MAC 的算法(例如 HS256HS384HS512),与 client-id 相对应的 client-secret 用作签名验证的对称密钥。

For MAC-based algorithms (such as HS256, HS384, or HS512), the client-secret that corresponds to the client-id is used as the symmetric key for signature verification.

如果为 OpenID Connect 1.0 身份验证配置了多个 ClientRegistration,则 JWS 算法解析器可能会评估提供的 ClientRegistration 来确定要返回哪个算法。

If more than one ClientRegistration is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided ClientRegistration to determine which algorithm to return.

然后,你可以继续配置logout

Then, you can proceed to configure logout