Смешанный токен доступа в приложении Spring WebMVC с использованием Spring Security oAuthClient
Я использую Spring Spring Boot / Security 5.4.8 с oAuth2-Client.
В моем приложении есть фоновая обработка (пул потоков с 3 параллельными заданиями). Часть каждого задания использует удаленную службу ReST
- который требует Jwt-аутентификации
- в зависимости от контекста арендатора задания, необходима другая область действия (если задание обрабатывается для арендатора A, область действия должна быть A и так далее ...)
В тривиальном сценарии обрабатываются только задания одного клиента, и токен доступа можно использовать повторно все время (до обновления). Это прекрасно работает.
Но при работе с разными арендаторами токен доступа должен быть изменен. К сожалению, токены доступа перепутались. Означает, что запрос задания 1 (арендатор A) содержит AT арендатора B и так далее. Я не наблюдал какого-либо детерминированного поведения, когда работает сопоставление токенов доступа и когда оно смешивается.
Мой полосатый код выглядит следующим образом.
У вас есть предложения, как подойти к этой проблеме? Или я совершаю какие-то мыслительные неудачи?
@Configuration
public class CommonConfig {
@Bean
OAuth2AuthorizedClientManager defaultAuthClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService) {
OAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();
var authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, oAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
authorizedClientManager.setAuthorizationSuccessHandler((authorizedClient, principal, attributes) ->
oAuth2AuthorizedClientService.saveAuthorizedClient(authorizedClient, principal));
authorizedClientManager.setAuthorizationFailureHandler(
new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
(clientRegistrationId, principal, attributes) ->
oAuth2AuthorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName())));
return authorizedClientManager;
}
@Bean(name = "threadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
return executor;
}
@Bean
public ClientRegistrationRepository clientRegistrationRepository(OAuth2ClientProperties properties) {
final List<ClientRegistration> registrations = new ArrayList<>(OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
final ClientRegistrationRepository parent = new InMemoryClientRegistrationRepository(registrations);
return (registrationId) -> {
final ClientRegistration clientRegistration = parent.findByRegistrationId("TEMPLATE");
if (clientRegistration == null) {
return null;
}
return ClientRegistration.withClientRegistration(clientRegistration)
.registrationId(registrationId)
.scope(resultingScopes)
.build();
};
}
@Component
public class JobScheduler {
@Scheduled(cron = "0/10 * * * * *")
public void trigger() {
List<Job> jobs = this.mockService.getJobs();
jobs.forEach(job -> this.jobExecutor.process(job));
}
}
@Component
public class JobExecutor {
@Async("threadPoolTaskExecutor")
public CompletableFuture<JobState> processJob(Job job) {
try{
SecurityContextHolder.getContext().setAuthentication(job.getAuth());
this.myService.getMyRemoteServiceResponse(job.getId());
} finally {
SecurityContextHolder.getContext().setAuthentication(null);
}
}
}
@Service
public class MyService {
public MyService(WebClient.Builder webClientBuilder) {
...
}
private WebClient getWebClient(String scope) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId(scope);
return webClientBuilder
.apply(oauth2Client.oauth2Configuration())
.build();
}
public String getMyRemoteServiceResponse(long id) {
String tenantName = SecurityContextHolder.getContext().getAuthentication().getName();
return this.getWebClient(tenantName)
.method(httpMethod)
.uri(uri)
.attributes(oauth2AuthorizedClient(this.getAuthorizedClient(this.getAuthenticationName())))
.retrieve()
.bodyToMono(String.class)
.block();
}
}