【问题标题】:How to prevent multiple authentication calls via WebClient如何防止通过 WebClient 进行多次身份验证调用
【发布时间】:2021-07-03 17:14:06
【问题描述】:

以下代码将获取 20 台设备,并将使用在线服务中的一些数据。

为了使用在线服务,WebClient 需要获取访问令牌(authorization-grant-type=client_credentials)

spring.security.oauth2.client.registration.web.client-id=xxxx
spring.security.oauth2.client.registration.web.client-secret=xxxx
spring.security.oauth2.client.registration.web.scope=xxxx
spring.security.oauth2.client.registration.web.authorization-grant-type=client_credentials
spring.security.oauth2.client.provider.web.token-uri=https://xxxx

配置:

@Configuration
public class Config {

@Autowired
public ClientRegistrationRepository clientRegistrationRepository;

@Bean
public WebClient webClient(ExchangeFilterFunction getOAuth2FilterFunction) {
    logger.info("** start webClient **");
    
    return WebClient.builder().filter(getOAuth2FilterFunction).build();
}

@Bean
public ExchangeFilterFunction getOAuth2FilterFunction(ReactiveClientRegistrationRepository clientRegistrationRepository) {
    logger.info("** start getOAuth2FilterFunction **");
    
    InMemoryReactiveOAuth2AuthorizedClientService authorizedClientService = new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
    AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientService);
    authorizedClientManager.setAuthorizedClientProvider(new ClientCredentialsReactiveOAuth2AuthorizedClientProvider());

    ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2FilterFunction = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
    oauth2FilterFunction.setDefaultClientRegistrationId("web");
    return oauth2FilterFunction;
}

@Bean
public ReactiveClientRegistrationRepository clientRegistrations() {
    logger.info("** start clientRegistrations **");
    
    ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId("web");
    return new InMemoryReactiveClientRegistrationRepository(clientRegistration);
}

网络客户端:

private void getData(List<Device> initlist) {
    logger.info("** start getDeviceData **");
    
    List<Mono<DeviceDto>> jsonDeviceList = initlist.stream()
            .map(device -> webClient.post().uri(infoUri)
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                    .body(Mono.just(device.getMacAddress()),String.class)
                    .attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId("web"))
                    .retrieve()
                    .bodyToMono(DeviceDto.class))
            .collect(Collectors.toList());

    ObjectMapper mapper = new ObjectMapper();
    Flux<DeviceDto> mergedMonos = Flux.fromIterable(jsonDeviceList).flatMapSequential(Function.identity());
    mergedMonos.map(device -> mapper.valueToTree(device)).collectList().subscribe(list -> {
        generateCsv(list);
    });
}

问题在于,对于每个服务请求,都会有来自身份验证服务器的等效访问令牌请求。因此,对于列表中的所有 20 台设备,WebClient 将请求访问令牌 20 次。

如何将其更改为只有一个呼叫而不是 20 个?

谢谢

【问题讨论】:

  • 你试过什么?
  • 我可以通过DEBUG模式看到有20个auth调用
  • 这不是我的问题,我的问题是,您编写了哪些代码来尝试解决问题。
  • 无论您在帖子中看到什么
  • 您已经编写了一个拦截过滤器,它将拦截每个调用 oauth 服务器的请求。 ofc 它将为每个请求执行一个请求。如果您不想这样做,请先执行一个请求,然后再执行 20 个请求.... 使用常规 Web 客户端。

标签: java spring-security spring-webflux spring-security-oauth2


【解决方案1】:

@Toerktumlare 说的是对的。如果你想调用一次oauth服务器,你只需要在第一次请求时将access_token缓存在内存或redis中,然后在过期之前使用相同的access_token。

WebClient webClient = WebClient.builder()
        .defaultHeader("Authorization", "Bearer " + getAccessToken())
        .build();

private String getAccessToken() {
    // get access_token from redis
    // if expiring, it will return null
    String access_token = (String) redisTemplate.opsForValue().get("access_token");
    if (null != access_token) {
        return access_token;
    }
    // get access_token from oauth server again and cache it into redis
    return getAccessTokenFromOauthServer();
}

【讨论】:

  • 如何获取令牌?
  • 从您的 oauth 服务器获取令牌。
  • 谢谢 Holinc,是的,从服务器获取令牌并通过 WebClient 发送带有令牌的成功请求的过程运行良好。但是我怎样才能打印令牌?我需要令牌才能通过另一个函数将其添加到请求中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-16
  • 2014-01-29
  • 2015-11-08
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多