Fehler in Spring Cloud GatewayFilter, wenn mehrere asynchrone Aufrufe zum Aktualisieren des Zugriffstokens durchgeführt Java

Java-Forum
Guest
 Fehler in Spring Cloud GatewayFilter, wenn mehrere asynchrone Aufrufe zum Aktualisieren des Zugriffstokens durchgeführt

Post by Guest »

Ich habe ein API-Gateway, das Spring Cloud Gateway im Autorisierungscodefluss verwendet.
Der oAuth-Server stellt bei der Anmeldung ein Aktualisierungs- und Zugriffstoken bereit, aber das Aktualisierungstoken kann nur einmal verwendet werden, um ein neues Zugriffstoken und ein neues Zugriffstoken zu erhalten Neues Aktualisierungstoken bei Ablauf, und danach wird das vorherige Aktualisierungstoken widerrufen.
Dies funktioniert gut, wenn der Webclient Anfragen in normaler Geschwindigkeit sendet. Es scheint jedoch, dass wir auf eine Race-Bedingung gestoßen sind, wenn das Zugriffstoken abgelaufen ist und mehrere asynchrone Aufrufe stattfinden. Wenn zwei oder mehr Anfragen gleichzeitig vom Browser gestellt werden, löst Anfrage 1 eine Aktualisierung beim Autorisierungsserver aus. Bevor Anfrage 1 abgeschlossen ist, erreicht Anfrage 2 das Gateway und löst eine weitere Aktualisierung aus. Die erste Anfrage wird erfolgreich sein, aber die zweite wird fehlschlagen, weil das Aktualisierungstoken widerrufen wurde.
Gibt es eine Konfiguration, die dieses Problem beheben kann, oder kann ich Änderungen am Filter vornehmen? ? Unten ist mein Filter, und er erhält den Fehler in der Methode „authorizeClient“.

Hinweis: Ich kann keine Änderungen daran vornehmen SSO-Server, da dies eine Unternehmensrichtlinie ist, die das Aktualisierungstoken nach seiner Verwendung widerruft.


Ich verwende Spring-Boot v3.3.3 und Spring-Cloud-Gateway v4.1.5

Code: Select all

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.ClientAuthorizationException;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
import com.abc.service.UserService;
import java.time.Duration;

@Component
@Slf4j
public class CustomTokenRelayGatewayFilterFactory extends AbstractGatewayFilterFactory {
private final ReactiveOAuth2AuthorizedClientManager authorizedClientManager;

private final UserService userService;

public CustomTokenRelayGatewayFilterFactory(final ServerOAuth2AuthorizedClientRepository authorizedClientRepository,
final ReactiveClientRegistrationRepository clientRegistrationRepository,
final UserService userService) {
super(Object.class);
userService = userService;
authorizedClientManager = generateDefaultAuthorizedClientManager(clientRegistrationRepository,
authorizedClientRepository);
}

private ReactiveOAuth2AuthorizedClientManager generateDefaultAuthorizedClientManager(
final ReactiveClientRegistrationRepository clientRegistrationRepository,
final ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
final Duration tokenClockSkewDuration = Duration.ofSeconds(5);
final ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider
= ReactiveOAuth2AuthorizedClientProviderBuilder.builder().authorizationCode()
.refreshToken(configurer -> configurer.clockSkew(tokenClockSkewDuration))
.clientCredentials(configurer -> configurer.clockSkew(tokenClockSkewDuration))
.password(configurer -> configurer.clockSkew(tokenClockSkewDuration)).build();
final DefaultReactiveOAuth2AuthorizedClientManager defaultAuthorizedClientManager
= new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository,
authorizedClientRepository);
defaultAuthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

return defaultAuthorizedClientManager;
}

@Override
public GatewayFilter apply(final Object config) {
return (exchange, chain) ->
exchange.getSession().flatMap(mapSession ->
exchange.getPrincipal().log("token-relay-filter")
.filter(principal -> principal instanceof OAuth2AuthenticationToken)
.cast(OAuth2AuthenticationToken.class)
.flatMap(this::authorizeClient)
.map(auth2AuthenticationToken ->  auth2AuthenticationToken.getName())
.flatMap(userService::getRoles)
.flatMap(userRoles -> this.withBearerAuth(exchange, userRoles))
.onErrorResume(ClientAuthorizationException.class,
e -> Mono.defer(() -> exchange.getSession()
.map(WebSession::invalidate))
.map(a -> exchange))
.defaultIfEmpty(exchange)
.flatMap(chain::filter)

);
}

Mono withBearerAuth(final ServerWebExchange exchange, final UserRoles userRoles) {
// add custom jwt token in auth header
}

Mono authorizeClient(final OAuth2AuthenticationToken oAuth2AuthenticationToken) {
final String clientRegistrationId = oAuth2AuthenticationToken.getAuthorizedClientRegistrationId();

return Mono.defer(() -> authorizedClientManager.authorize(
createOAuth2AuthorizeRequest(clientRegistrationId, oAuth2AuthenticationToken)))
.map(oAuth2AuthorizedClient -> oAuth2AuthenticationToken);
}

private OAuth2AuthorizeRequest createOAuth2AuthorizeRequest(final String clientRegistrationId,
final Authentication principal) {
return OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId).principal(principal).build();
}
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post