如何使用 spring-boot-starter-oauth2-client 执行刷新

Posted

技术标签:

【中文标题】如何使用 spring-boot-starter-oauth2-client 执行刷新【英文标题】:How to perform a refresh with spring-boot-starter-oauth2-client 【发布时间】:2021-10-24 05:18:40 【问题描述】:

我正在使用 spring-boot-starter-oauth2-client 向 Google 验证我的用户。这很好用,我可以按预期登录并获得有效的访问权限和刷新令牌。

我正在创建访问令牌:

public class TokenServiceImpl implements TokenService 

    private final OAuth2AuthorizedClientService clientService;

    @Override
    public GoogleCredentials credentials() 
        final var accessToken = getAccessToken();

        return getGoogleCredentials(accessToken);
    

    private GoogleCredentials getGoogleCredentials(String accessToken) 

        return GoogleCredentials
                .newBuilder()
                .setAccessToken(new AccessToken(accessToken, null))
                .build();
    

    private String getAccessToken() 
        final var oauthToken = (OAuth2AuthenticationToken) SecurityContextHolder.getContext().getAuthentication();

        return clientService.loadAuthorizedClient(
                oauthToken.getAuthorizedClientRegistrationId(),
                oauthToken.getName()).getAccessToken().getTokenValue();
    

令牌最终被用于 Google Photo API 客户端

    private PhotosLibraryClient getClient() 
        
        final var settings =
                PhotosLibrarySettings
                        .newBuilder()
                        .setCredentialsProvider(FixedCredentialsProvider.create(tokenService.credentials()))
                        .build();

        return PhotosLibraryClient.initialize(settings);
    

问题是令牌将在短时间内过期,我想刷新它以使其保持活动状态。

我不确定我可以使用哪种方法模式来执行此操作,而不必编写整个 OAuth 流程(违背了 Spring oauth2-client 之类的目的)。

到目前为止,我的应用程序中没有其他令牌/安全/过滤器逻辑。

我只需要手动写出来,还是有其他方法可以做到这一点?

【问题讨论】:

可能有设置。您应该检查OAuth2AuthorizedClientService 实现。您正在使用的服务只存储它看起来的信息。可能有更高的组件可以使用。 【参考方案1】:

OAuth2AuthorizedClientManager 会为你刷新你的访问令牌,假设你得到一个刷新令牌和你的访问令牌。 OAuth2AuthorizedClientManager 的文档位于

https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client

在配置您的 OAuth2AuthorizedClientManager 时,请确保您已在 OAuth2AuthorizedClientProvider 中包含 refreshToken...

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientRepository authorizedClientRepository) 

    OAuth2AuthorizedClientProvider authorizedClientProvider =
            OAuth2AuthorizedClientProviderBuilder.builder()
                    .authorizationCode()
                    .refreshToken()
                    .build();

    DefaultOAuth2AuthorizedClientManager authorizedClientManager =
            new DefaultOAuth2AuthorizedClientManager(
                    clientRegistrationRepository, authorizedClientRepository);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    // Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
    // map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
    authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());

    return authorizedClientManager;

然后您使用 OAuth2AuthorizedClientManager 获取访问令牌。下面是来自 spring doco 的示例...

@Controller
public class OAuth2ClientController 

    @Autowired
    private OAuth2AuthorizedClientManager authorizedClientManager;

    @GetMapping("/")
    public String index(Authentication authentication,
                        HttpServletRequest servletRequest,
                        HttpServletResponse servletResponse) 

        OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attributes(attrs -> 
                    attrs.put(HttpServletRequest.class.getName(), servletRequest);
                    attrs.put(HttpServletResponse.class.getName(), servletResponse);
                )
                .build();
        OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);

        OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

        ...

        return "index";
    

如果当前的accessToken已经过期,这会自动使用之前获取的refreshToken请求一个新的accessToken。

【讨论】:

以上是关于如何使用 spring-boot-starter-oauth2-client 执行刷新的主要内容,如果未能解决你的问题,请参考以下文章

Spring-boot-starter RabbitMQ 全局错误处理

spring-boot-starter家族成员简介

spring-boot-starter家族成员简介

如何在优雅地Spring 中实现消息的发送和消费

SpringBoot的spring-boot-starter有哪些(官方)

[redis分布式锁]redisson分布式锁的实现及spring-boot-starter封装