高级配置

HttpSecurity.oauth2Login() 提供了许多配置选项,用于自定义 OAuth 2.0 登录。主要配置选项根据其协议端点对应关系进行分组。

例如,oauth2Login().authorizationEndpoint() 允许配置 授权端点,而 oauth2Login().tokenEndpoint() 允许配置 令牌端点

以下代码显示了一个示例

高级 OAuth2 登录配置
  • 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 的主要目标是与规范中定义的命名保持紧密一致。

OAuth 2.0 授权框架将 协议端点 定义如下

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

  • 授权端点:客户端通过用户代理重定向从资源所有者获取授权。

  • 令牌端点:客户端用于交换授权许可以获取访问令牌,通常伴随客户端认证。

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

  • 重定向端点:授权服务器通过资源所有者用户代理将包含授权凭证的响应返回给客户端。

OpenID Connect Core 1.0 规范将 UserInfo 端点 定义如下

UserInfo 端点是一个 OAuth 2.0 受保护资源,返回有关已认证最终用户的信息。为了获取有关最终用户的请求信息,客户端通过使用通过 OpenID Connect 认证获得的访问令牌向 UserInfo 端点发出请求。这些信息通常由一个 JSON 对象表示,该对象包含一组键值对。

以下代码显示了 oauth2Login() DSL 的所有可用配置选项

OAuth2 登录配置选项
  • 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 配置。

以下代码显示了 security namespace 中所有可用的配置选项

OAuth2 登录 XML 配置选项
<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>

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

OAuth 2.0 登录页面

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

为了让 DefaultLoginPageGeneratingFilter 显示配置的 OAuth 客户端的链接,注册的 ClientRegistrationRepository 也需要实现 Iterable<ClientRegistration>。请参阅 InMemoryClientRegistrationRepository 以供参考。

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

OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{registrationId}"

以下行显示了一个示例

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

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

以下列表显示了一个示例

OAuth2 登录页面配置
  • 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>

您需要提供一个带有 @RequestMapping("/login/oauth2")@Controller,它能够渲染自定义登录页面。

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

以下行显示了一个示例

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

重定向端点

重定向端点由授权服务器用于通过资源所有者用户代理将授权响应(包含授权凭证)返回给客户端。

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

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

如果您想自定义授权响应 baseUri,请按以下方式配置它

重定向端点配置
  • 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 匹配。

以下列表显示了一个示例

  • Java

  • Kotlin

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

UserInfo 端点

UserInfo 端点包括许多配置选项,如下面子节所述

映射用户权限

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

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

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

使用 GrantedAuthoritiesMapper

GrantedAuthoritiesMapper 接收一个权限列表,其中包含一个类型为 OAuth2UserAuthority 的特殊权限和权限字符串 OAUTH2_USER(或 OidcUserAuthority 和权限字符串 OIDC_USER)。

提供 GrantedAuthoritiesMapper 的实现并按如下方式配置它

授予权限映射器配置
  • 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,使其自动应用于配置,如下所示

授予权限映射器 Bean 配置
  • 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 {
        ...
    }
}

身份验证完成后,它还包含 FACTOR_AUTHORIZATION_CODE 授予权限。

基于委托的 OAuth2UserService 策略

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

OAuth2UserRequest(和 OidcUserRequest)允许您访问关联的 OAuth2AccessToken,这在 委托者 需要从受保护资源获取权限信息才能为用户映射自定义权限的情况下非常有用。

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

OAuth2UserService 配置
  • 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 提供者。

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

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

如果您需要自定义 UserInfo 请求的预处理,可以通过自定义的 Converter<OAuth2UserRequest, RequestEntity<?>> 提供给 DefaultOAuth2UserService.setRequestEntityConverter()。默认实现 OAuth2UserRequestEntityConverter 构建了 UserInfo 请求的 RequestEntity 表示,默认情况下将 OAuth2AccessToken 设置在 Authorization 头中。

另一方面,如果您需要自定义 UserInfo 响应的后处理,您需要向 DefaultOAuth2UserService.setRestOperations() 提供一个自定义配置的 RestOperations。默认的 RestOperations 配置如下

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

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

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

  • 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 在 UserInfo 端点请求用户属性时利用 DefaultOAuth2UserService

如果您需要自定义 UserInfo 请求的预处理或 UserInfo 响应的后处理,您需要向 OidcUserService.setOauth2UserService() 提供一个自定义配置的 DefaultOAuth2UserService

无论您是自定义 OidcUserService 还是为 OpenID Connect 1.0 提供者提供自己的 OAuth2UserService 实现,都需要按以下方式配置它

  • 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 令牌签名验证

OpenID Connect 1.0 认证引入了 ID 令牌,它是一个安全令牌,在客户端使用时包含有关授权服务器对最终用户进行认证的信息。

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

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

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

以下代码显示了如何配置 OidcIdTokenDecoderFactory @Bean,以便所有 ClientRegistration 实例默认使用 MacAlgorithm.HS256

  • Java

  • Kotlin

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

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

如果为 OpenID Connect 1.0 认证配置了多个 ClientRegistration,JWS 算法解析器可以评估提供的 ClientRegistration 以确定返回哪个算法。

然后,您可以继续配置 注销

© . This site is unofficial and not affiliated with VMware.