首页 > 解决方案 > 如何防止通过 WebClient 进行多次身份验证调用

问题描述

以下代码将获取 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 ?

谢谢

标签: javaspring-securityspring-webfluxspring-security-oauth2

解决方案


@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();
}

推荐阅读