![](/img/trans.png)
[英]Spring Authorization Server 1.0.0: invalid_client error while requesting /oauth2/token
[英]Getting invalid_client error when trying to revoke an access token in Spring Authorization Server
我正在使用Spring 授权服务器 0.3.1并从 OAuth 客户端(angular-oauth2-oidc)调用/oauth2/revoke端点。 有效载荷:
client_id: my-client-id
token: eyJraWQiOiJlYzA3N2Y2OC1jMjQ1LTQ[the rest is stripped for readability]
token_type_hint: access_token
响应包含错误“invalid_client”。
几分钟的调试表明在 org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider#authenticate 方法中抛出了异常:
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication =
(OAuth2TokenRevocationAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientPrincipal =
getAuthenticatedClientElseThrowInvalidClient(tokenRevocationAuthentication);
getAuthenticatedClientElseThrowInvalidClient 方法:
static OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) {
OAuth2ClientAuthenticationToken clientPrincipal = null;
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
}
if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
return clientPrincipal;
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
}
似乎 OAuth2TokenRevocationAuthenticationProvider 期望通过身份验证的 object 主体中会有 OAuth2ClientAuthenticationToken,但实际上主体是 JwtAuthenticationToken 的一个实例。 据我了解,由于调用具有承载令牌,因此已获得授权,并且 JwtAuthenticationToken 设置为 SecurityContextHolder:
2022-06-28 23:27:01.392 DEBUG 16904 --- [ XNIO-1 task-1] o.s.security.web.FilterChainProxy : Securing POST /oauth2/revoke
2022-06-28 23:27:01.393 DEBUG 16904 --- [ XNIO-1 task-1] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-06-28 23:27:01.394 DEBUG 16904 --- [ XNIO-1 task-1] o.s.s.o.s.r.a.JwtAuthenticationProvider : Authenticated token
2022-06-28 23:27:01.397 DEBUG 16904 --- [ XNIO-1 task-1] .o.s.r.w.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@1615fb2d, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[SCOPE_openid, SCOPE_profile, SCOPE_offline_access, SCOPE_email]]
2022-06-28 23:27:01.398 DEBUG 16904 --- [ XNIO-1 task-1] o.s.s.w.a.i.FilterSecurityInterceptor : Authorized filter invocation [POST /oauth2/revoke] with attributes [authenticated]
我错过了什么吗? 如何在这里获取 OAuth2ClientAuthenticationToken 而不是 JwtAuthenticationToken?
设法解决了问题。 发生这种情况是因为我在授权过滤器链设置中有oauth2ResourceServer() (以启用 userInfo 端点)并在 /oauth2/revoke 调用的 header 中发送了承载令牌:
@Bean
@Order(2)
@SuppressWarnings("unused")
public SecurityFilterChain authorizationFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer<>();
RequestMatcher authServerEndpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
// Custom User Info Mapper that retrieves claims from a signed JWT
Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = context -> {
OidcUserInfoAuthenticationToken authentication = context.getAuthentication();
JwtAuthenticationToken principal = (JwtAuthenticationToken) authentication.getPrincipal();
return new OidcUserInfo(principal.getToken().getClaims());
};
if (StringUtils.isNotEmpty(ldapProperties.getUrl())) {
http.authenticationProvider(ldapAuthenticationProvider());
}
http.authenticationProvider(daoAuthenticationProvider());
http
.requestMatchers((matchers) -> matchers.requestMatchers(authServerEndpointsMatcher)
.mvcMatchers("/login**", "/saml2/**"))
.authorizeRequests()
.antMatchers("/login/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) // to enable userInfo endpoint
.apply(authorizationServerConfigurer)
.oidc(oidc -> oidc
.clientRegistrationEndpoint(Customizer.withDefaults())
.userInfoEndpoint(userInfo -> userInfo.userInfoMapper(userInfoMapper))
).and()
;
if (authProperties.getSso().isEnabled()) {
http
.saml2Login().successHandler(new SavedRequestAwareAuthenticationSuccessHandler()).permitAll()
.and()
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.saml2Logout(Customizer.withDefaults());
} else {
http.formLogin().loginPage("/login").permitAll();
}
return http.build();
}
因此,解决方案是删除承载令牌 header 或删除此特定过滤器链中的 oauth2ResourceServer()。
我还发现,截至目前,Spring 授权服务器仅支持 /oauth2/revoke 端点的基本授权,如果没有在基本身份验证 header 中指定客户端密码,则无法调用此端点。 当您有公共客户时,这是一个问题。 例如,Keycloak 提供了一个选项来在请求正文以及基本身份验证 header 中指定客户端凭据,并允许省略客户端密码参数
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.