Problem with Spring SecurityContext

I have a MEssageDispatchInterceptor:

`

private MessageDispatchInterceptor<Message<?>> AuthenticationInterceptor() {
return list -> {
if ( isInitDraftCommand(list.get(0)) ) {
return (index, message) -> message;
}

final PolluxUserDetails user = SecurityUtils.getUser();
if (Objects.nonNull(user)) {
return (index, message) -> message.andMetaData( Collections.singletonMap(“user”, user) );
}

throw new SecurityException(“User not found in context”);
};
}

`

And into a spring service a method with @EventHandler:

`

@Service(“managerService”)
public class ManagerService {
@EventHandler
protected void loadDraft(final LoadDraftEvent event) throws DraftException {
final CompletableFuture loadStationFuture = CompletableFuture.supplyAsync(() -> {
for(Station station: getStations() {
commandGateway.send(new InitStationDraftCommand(stationId, draft.getDraftId(), station));
}
return true;
}

final CompletableFuture loadAreaFeature = CompletableFuture.supplyAsync(() -> {
for(Area area: getAreas() {
commandGateway.send(new InitAreaDraftCommand(areaId, draft.getDraftId(), area));
}
return true;
}

Stream.of(loadStationFuture, loadAreaFeature)
.map(CompletableFuture::join).reduce((a, b) -> a && b)
.orElseThrow(() -> new DraftException("Error when loading Draft: " + draft.getDraftId()));
}
}

`

The problem is on Interceptor the user that I get from SecurityContextHolder.getContext() is null on the first CompletableFuture but not in the second depends on which task start first.
Why this? on my configuration I have: @EnableAsync and on config class constructor SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

Hi Marco,

Several factors might influence whether the same thread is used to publish your commands and events.
The SecurityContext is not automatically copied over from one thread to another; you’ll have to introduce handler and dispatch interceptors for this yourself.

A vanilla Axon 3 application with the Spring Boot starter, thus without any configuration in place, will typically not jump from one thread to another.

Axon 4 defaults are however different, as there the Tracking Event Processor is used for handling events.

The Tracking Event Processor handles events in a different thread from the one publishing that event by definition.

Thus, I’d suggest to introduce a MessageDispatchInterceptor to put your user info in the metadata of the message your publishing, and a MessageHandlerInterceptor to pull this info from the metadata and populate your SecurityContext on the handling side.

Hope this helps!

Cheers,
Steven

Thanks Steven.

I already use MessageDispatchInterceptor to add user on command metadata.

I think the problem is on method with @EventHandler where in respose of event I send in pararrell different command. Probability best solution is to use a Saga to manage this type dispatch

Hi Marco,

I just now noted that, in your snippet, you’re using CompletableFuture.supplyAsync to dispatch a command on the gateway.
To make command dispatching asynchronous, I’d suggest configuring the AsynchronousCommandBus instead.

That way you will leverage the benefit of asynchronous dispatching, but also keep being able to grasp the SecurityContext correctly like I’ve suggested earlier with the message handler/dispatch interceptors you already have in place (great job by the way!).

I’d assume that should fix the problem you’re encountering.

Hope this helps, let me/us know if it doesn’t!

Cheers,
Steven