我正在使用带有 oAuth2-Client 的 spring Spring Boot / Security 5.4.8。
我的应用程序具有后台处理(具有 3 个并行作业的线程池)。每个作业的一部分都在使用远程 ReST 服务
- 这需要 Jwt 身份验证
- 根据作业的租户上下文,需要不同的范围(如果为租户 A 处理作业,范围必须是 A,依此类推...)
在一个简单的场景中,只处理一个租户的作业,并且访问令牌可以一直重复使用(直到刷新)。这工作正常。
但是对于不同租户的工作,必须更改访问令牌。不幸的是,访问令牌混淆了。表示作业 1(租户 A)的请求包含租户 B 的 AT,依此类推。当访问令牌映射工作以及混淆时,我没有观察到任何确定性行为。
我的条带化代码如下所示。
你对如何解决这个问题有什么建议吗?还是我在做任何概念上的失败?
@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();
}
}