首页 > 解决方案 > 如何在 Spring OAuth2 客户端中获取刷新令牌

问题描述

我正在开发一个充当 OAuth2 客户端的 Spring 应用程序,而 Spotify 是资源服务器。这是我的配置:

spring:
  security:
    oauth2:
      client:
        registration:
          spotify:
            client-id: ...
            client-secret: ...
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            scope: user-read-private, user-read-email
            client-name: Spotify
            client-alias: spotify
        provider:
          spotify:
            authorization-uri: https://accounts.spotify.com/authorize
            token-uri: https://accounts.spotify.com/api/token
            user-info-uri: https://api.spotify.com/v1/me
            user-name-attribute: display_name

我的问题是我无法找到如何获取 Spotify 在响应中发送的刷新令牌/api/token

这就是 Spotify 响应的样子:(来源:https ://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow )

发现响应

我试图CustomUserService像这样实现我自己的:

.and()
  .userInfoEndpoint()
  .userService(customUserService)

在我的内部,我CustomUserService尝试重载以下方法:public OAuth2User loadUser(OAuth2UserRequest userRequest)

在这个 OAuth2UserRequest 对象中,我可以找到访问令牌,但绝对没有关于刷新令牌的信息:

访问令牌

我正在考虑我需要一些额外的配置来将 refresh_token 放入additionalParameters对象中,但我找不到这样的东西。

有什么办法可以在我的代码中获取刷新令牌并用它做一些事情吗?

标签: springoauth-2.0jwt

解决方案


所以我想出了一个办法来克服这个问题。首先需要的是accessTokenResponseClient在安全配置中包含自定义实现。

安全配置:

...
.and()
  .tokenEndpoint()
  .accessTokenResponseClient(accessTokenResponseClient())
...

这里的关键部分是设置我们的CustomTokenResponseConverter

@Bean
  public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
    DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
        new DefaultAuthorizationCodeTokenResponseClient();

    OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter =
        new OAuth2AccessTokenResponseHttpMessageConverter();
    tokenResponseHttpMessageConverter.setTokenResponseConverter(new CustomTokenResponseConverter());
    RestTemplate restTemplate = new RestTemplate(Arrays.asList(
        new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
    restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

    accessTokenResponseClient.setRestOperations(restTemplate);
    return accessTokenResponseClient;
  }

在此转换器中,可以访问刷新令牌,例如将其放入additionalParameters问题中提到的地图中:

public class CustomTokenResponseConverter implements
    Converter<Map<String, String>, OAuth2AccessTokenResponse> {

  @Override
  public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
    String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
    String refreshToken = tokenResponseParameters.get(OAuth2ParameterNames.REFRESH_TOKEN);
    long expiresIn = Long.parseLong(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));

    Set<String> scopes = Collections.emptySet();
    if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
      String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
      scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " "))
          .collect(Collectors.toSet());
    }

    Map<String, Object> additionalParameters = new HashMap<>();
    additionalParameters.put(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken);

    return OAuth2AccessTokenResponse.withToken(accessToken)
        .tokenType(OAuth2AccessToken.TokenType.BEARER)
        .expiresIn(expiresIn)
        .scopes(scopes)
        .refreshToken(refreshToken)
        .additionalParameters(Collections.unmodifiableMap(additionalParameters))
        .build();
  }
}

这样可以通过additionalParameters以下方式在自定义用户服务中访问它:

String refreshToken = (String) userRequest.getAdditionalParameters().get(OAuth2ParameterNames.REFRESH_TOKEN);

推荐阅读