Spring Security Spring Cloud Gateway在尝试使用过期的access_token和refresh_token进行refresh_token时出现500异常

mf98qq94  于 2022-11-24  发布在  Spring
关注(0)|答案(1)|浏览(271)

我有一个使用ServerHttpSecurity.oauth2Login()的安全Spring Cloud Gateway应用程序,该应用程序可以使用refresh token成功续订过期的访问令牌。但是,当刷新令牌也过期并且应用程序尝试使用它续订访问令牌时,我收到一个500内部服务器错误[似乎是由它之前的400 Bad Request错误引起的],并出现以下异常:

org.springframework.security.oauth2.client.ClientAuthorizationException: [invalid_grant] Token is not active
    at org.springframework.security.oauth2.client.RefreshTokenReactiveOAuth2AuthorizedClientProvider.lambda$authorize$0(RefreshTokenReactiveOAuth2AuthorizedClientProvider.java:97) ~[spring-security-oauth2-client-5.4.1.jar:5.4.1]

完整日志位于:https://github.com/spring-projects/spring-security/files/8319348/logs.txt
仅当我重新发出请求(使用对安全端点的调用刷新浏览器)时,我才会被重定向到登录页面**(预期行为)。**
在调试时,我注意到在500内部服务器错误之后重新发出请求会导致以下异常:

org.springframework.security.oauth2.client.ClientAuthorizationRequiredException: [client_authorization_required] Authorization required for Client Registration Id: <client-id>.

这可能是导致重定向到登录页面的原因。
Request execution details here
我的问题:我是否可以避免500内部服务器错误而被重定向到登录页面?如果可以,我该如何实现?

环境详细信息Spring Boot :2.4.0Spring Cloud:2020.0.0 Spring 安全:5.4.1

ndh0cuux

ndh0cuux1#

解决方案是捕获刷新令牌时导致的500,然后使用下面的类启动新的授权流:

import org.springframework.security.oauth2.client.*;
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.util.Assert;
import reactor.core.publisher.Mono;

/**
 * A delegating implementation of ReactiveOAuth2AuthorizedClientManager to help deal with a 500 Internal Server Error
 * that is a result of an expired access token. With ReactiveOAuth2AuthorizedClientManagerCustom, we manage to redirect
 * to the login page instead of returning a 500 Internal Server Error to the user/client.
 */
public class ReactiveOAuth2AuthorizedClientManagerCustom implements ReactiveOAuth2AuthorizedClientManager {

    private final ReactiveClientRegistrationRepository clientRegistrationRepository;
    private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;
    private final ReactiveOAuth2AuthorizedClientManager authorizedClientManager;

    public ReactiveOAuth2AuthorizedClientManagerCustom(ReactiveClientRegistrationRepository clientRegistrationRepository,
                                                       ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
        this.clientRegistrationRepository = clientRegistrationRepository;
        this.authorizedClientRepository = authorizedClientRepository;
        this.authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
                this.clientRegistrationRepository, this.authorizedClientRepository
        );
    }

    public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
        Assert.notNull(authorizeRequest.getClientRegistrationId(), "Client registration id cannot be null");

        return this.authorizedClientManager.authorize(authorizeRequest)
                // The token has expired, therefore we initiate a new grant flow
                .onErrorMap(
                        ClientAuthorizationException.class,
                        error -> new ClientAuthorizationRequiredException(authorizeRequest.getClientRegistrationId())
                );
    }
}

然后添加下一个@Bean

public ReactiveOAuth2AuthorizedClientManagerCustomConfig(ReactiveClientRegistrationRepository clientRegistrationRepository,
                                                             ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
        this.clientRegistrationRepository = clientRegistrationRepository;
        this.authorizedClientRepository = authorizedClientRepository;
    }

    @Bean
    @Primary
    ReactiveOAuth2AuthorizedClientManager authorizedClientManager() {
        return new ReactiveOAuth2AuthorizedClientManagerCustom(
                this.clientRegistrationRepository, this.authorizedClientRepository
        );
    }

相关问题