如何在 Spring OAuth2 客户端中获取刷新令牌

joh*_*yka 5 spring oauth-2.0 jwt

我正在开发一个 Spring 应用程序,它充当 OAuth2 客户端,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
Run Code Online (Sandbox Code Playgroud)

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

Spotify 的响应如下所示:(来源: https: //developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow

Spotify 响应

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

访问令牌

我在想我需要一些额外的配置来将刷新令牌放入对象中additionalParameters,但我找不到这样的东西。

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

joh*_*yka 2

所以我想出了一个办法来克服这个问题。需要做的第一件事是将其包含accessTokenResponseClient在具有自定义实现的安全配置中。

安全配置:

...
.and()
  .tokenEndpoint()
  .accessTokenResponseClient(accessTokenResponseClient())
...
Run Code Online (Sandbox Code Playgroud)

这里的关键部分是设置我们的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;
  }
Run Code Online (Sandbox Code Playgroud)

在此转换器中,可以访问刷新令牌,例如将其放入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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

String refreshToken = (String) userRequest.getAdditionalParameters().get(OAuth2ParameterNames.REFRESH_TOKEN);
Run Code Online (Sandbox Code Playgroud)