spring-security 从OAuth2 refresh_token设置Spring WebClient筛选器

8ehkhllq  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(188)

我不得不使用一个外部API,它使用OAuth2来保证安全性。它们不支持授权类型“client_credentials”,而是给予了一个长寿命的refresh_token,我们可以将其注入Spring应用程序,而不会使其过期。
但是,我找不到任何有关如何使用给定的refresh_token为WebClient设置OAuth2过滤器以获取access_token的信息。
下面是一些代码,以获得一个起点,并显示我已经尝试了什么。
第一个
有人需要做类似的事情,并有一个解决这个问题的办法吗?

q5iwbnjs

q5iwbnjs1#

是的,我做过类似的事情。我的上下文是(尝试)与Google Smart Device Management生态系统集成。我不确定我是否推荐它,但我至少成功地使用了刷新令牌来连接到API。
我可以想到两种方法来实现这一点:
1.自己实现ReactiveOAuth2AuthorizedClientService
1.使用内存中实现并在启动时加载refreshToken
这两种方法是相似的,因为在这两种情况下,我们都没有accessToken,必须给予框架一个过期的令牌,这样它就知道在第一次使用时加载一个新的令牌。
我选择了第二种选择,尽管事后看来,第一种选择可能更好。
下面是一个类,它将所有概念联系在一起,并允许您为基于请求的访问或不响应用户请求的后台服务(例如,计划的批处理作业)创建一个WebClient

@Component
public class WebClientFactory {

    private final ReactiveClientRegistrationRepository clientRegistrationRepository;

    private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;

    private final ReactiveOAuth2AuthorizedClientService authorizedClientService;

    private final ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;

    private final String baseUrl;

    public WebClientFactory(
            ReactiveClientRegistrationRepository clientRegistrationRepository,
            ServerOAuth2AuthorizedClientRepository authorizedClientRepository,
            ReactiveOAuth2AuthorizedClientService authorizedClientService,
            ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider,
            @Value("${my.base-url}") String baseUrl) {
        this.clientRegistrationRepository = clientRegistrationRepository;
        this.authorizedClientRepository = authorizedClientRepository;
        this.authorizedClientService = authorizedClientService;
        this.authorizedClientProvider = authorizedClientProvider;
        this.baseUrl = baseUrl;
    }

    public Mono<Void> saveRefreshToken(String registrationId, String principalName, String token) {
        var now = Instant.now();
        var accessToken = new OAuth2AccessToken(
            OAuth2AccessToken.TokenType.BEARER, "none", now.minusSeconds(2), now.minusSeconds(1));
        var refreshToken = new OAuth2RefreshToken(token, now);
        var principal = new BearerTokenAuthenticationToken(principalName);
        principal.setAuthenticated(true);

        return this.clientRegistrationRepository.findByRegistrationId(registrationId)
            .switchIfEmpty(Mono.error(new InternalAuthenticationServiceException("Unable to find registrationId " + registrationId)))
            .map((clientRegistration) -> new OAuth2AuthorizedClient(clientRegistration, principalName, accessToken, refreshToken))
            .flatMap((authorizedClient) -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal));
    }

    public WebClient createRequestClient() {
        var authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
            this.clientRegistrationRepository, this.authorizedClientRepository);
        authorizedClientManager.setAuthorizedClientProvider(this.authorizedClientProvider);

        return createWebClient(authorizedClientManager);
    }

    public WebClient createBackgroundClient() {
        var authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
            this.clientRegistrationRepository, this.authorizedClientService);
        authorizedClientManager.setAuthorizedClientProvider(this.authorizedClientProvider);

        return createWebClient(authorizedClientManager);
    }

    private WebClient createWebClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
        var oauth2 = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        return WebClient.builder()
            .filter(oauth2)
            .baseUrl(this.baseUrl)
            .build();
    }

}

**注意:**为了简单起见,我重新使用了spring-security-oauth2-resource-server中的BearerTokenAuthenticationToken,因为需要某种主体来唯一标识刷新令牌。

Sping Boot 将为您提供大多数注入的bean(默认为内存中),但您需要提供ReactiveOAuth2AuthorizedClientProvider

@Bean
    public ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider() {
        return ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .refreshToken()
            .build();
    }

如果在后台服务中使用WebClient,您需要通过ReactiveSecurityContextHolder自己设置与principal相同的BearerTokenAuthenticationToken
请参阅文档以了解更多信息。

相关问题