【问题标题】:Spring Cloud Gateway Getting a 500 Exception while trying to refresh_token using expired access_token and refresh_tokenSpring Cloud Gateway 在尝试使用过期的 access_token 和 refresh_token 刷新令牌时出现 500 异常
【发布时间】:2022-11-23 06:11:05
【问题描述】:

我有一个使用 ServerHttpSecurity.oauth2Login() 的安全 Spring Cloud Gateway 应用程序,它可以使用 refresh token 成功续订过期的访问令牌。然而,当刷新令牌也过期并且应用程序尝试用它更新访问令牌时,我收到 500 Internal Server Error[似乎是由之前的 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 Internal Server Error 后重新发出请求会导致以下异常:

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

这可能是导致重定向到登录页面的原因。

Request execution details here

我的问题:我可以避免收到 500 Internal Server Error 而被重定向到登录页面吗?如果是,我怎样才能做到这一点?

环境详情春季启动:2.4.0 春云:2020.0.0 春季安全:5.4.1

【问题讨论】:

  • 我面临同样的问题,你找到解决方案了吗?谢谢
  • 嗨@diego.gazzola,添加了一个答案,希望它有所帮助!

标签: spring-security oauth-2.0 spring-security-oauth2 spring-cloud-gateway refresh-token


【解决方案1】:

解决方案是捕获刷新令牌时引起的 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
        );
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    • 2020-05-20
    • 1970-01-01
    • 1970-01-01
    • 2018-01-28
    • 2016-03-07
    • 1970-01-01
    相关资源
    最近更新 更多