This sample demonstrates how to append the current username to outgoing messages and how to extract that value when messages are handled. The current principal is made available by using a principal accessor registered through dependency injection.
NOTE This sample doesn't use Thread.
because of the behavior of Thread.
in combination with asynchronous code is dependent on the framework version the code is executed on. For more information, refer to the excellent guideline Migrate from ClaimsPrincipal.Current of ASP.NET Core.
Fake principal
For demonstration purposes, before sending a message, the principalAccessor.
is replaced with a new instance. In a production scenario, the principalAccessor.
would be either the impersonated user from IIS by assigning Thread.
to it or the current user sending a message.
async Task SendMessage(int userNumber)
{
var identity = new GenericIdentity($"FakeUser{userNumber}");
principalAccessor.CurrentPrincipal = new GenericPrincipal(identity, new string[0]);
var message = new MyMessage();
await endpointInstance.Send("Samples.UsernameHeader.Endpoint2", message)
.ConfigureAwait(false);
}
await Task.WhenAll(SendMessage(1), SendMessage(2)).ConfigureAwait(false);
The snippet above uses two concurrent sends to demonstrate how the current principle is properly propagated into the message session.
Custom header with a mutator
The recommended approach for capturing the current user is to create a transport mutator that extracts the current identity and then adds it to the header of every outgoing message.
Outgoing message mutator
The outgoing mutator extracts principalAccessor.
and appends it to a message header.
public class AddUserNameToOutgoingHeadersMutator :
IMutateOutgoingTransportMessages
{
static ILog log = LogManager.GetLogger("Handler");
readonly IPrincipalAccessor principalAccessor;
public AddUserNameToOutgoingHeadersMutator(IPrincipalAccessor principalAccessor)
{
this.principalAccessor = principalAccessor;
}
public Task MutateOutgoing(MutateOutgoingTransportMessageContext context)
{
if (principalAccessor.CurrentPrincipal?.Identity.Name != null)
{
log.Info("Adding CurrentPrincipal user to headers");
context.OutgoingHeaders["UserName"] = principalAccessor.CurrentPrincipal.Identity.Name;
}
return Task.CompletedTask;
}
}
Register the outgoing message mutator
var principalAccessor = new PrincipalAccessor();
endpointConfiguration.RegisterComponents(
registration: components =>
{
components.RegisterSingleton<IPrincipalAccessor>(principalAccessor);
components.ConfigureComponent<AddUserNameToOutgoingHeadersMutator>(DependencyLifecycle.InstancePerCall);
});
Incoming message mutator
The incoming mutator extracts the username header from the message and set the principalAccessor.
.
public class SetCurrentPrincipalBasedOnHeaderMutator :
IMutateIncomingTransportMessages
{
static ILog log = LogManager.GetLogger("Handler");
readonly IPrincipalAccessor principalAccessor;
public SetCurrentPrincipalBasedOnHeaderMutator(IPrincipalAccessor principalAccessor)
{
this.principalAccessor = principalAccessor;
}
public Task MutateIncoming(MutateIncomingTransportMessageContext context)
{
if (context.Headers.TryGetValue("UserName", out var userNameHeader))
{
log.Info("Adding CurrentPrincipal user from headers");
var identity = new GenericIdentity(userNameHeader);
principalAccessor.CurrentPrincipal = new GenericPrincipal(identity, new string[0]);
}
return Task.CompletedTask;
}
}
Register the incoming message mutator
endpointConfiguration.RegisterComponents(c =>
{
c.RegisterSingleton<IPrincipalAccessor>(new PrincipalAccessor());
c.ConfigureComponent<SetCurrentPrincipalBasedOnHeaderMutator>(DependencyLifecycle.InstancePerCall);
});
This sample doesn't register the outgoing message mutator for the receiver. If desired, the outgoing message mutator could be registered on the receiver as well, which would automatically add the username header to all messages sent.
The Handler
From within a handler (or saga), this value can be used as follows:
public class HandlerUsingAccessor :
IHandleMessages<MyMessage>
{
static ILog log = LogManager.GetLogger("HandlerUsingAccessor");
readonly IPrincipalAccessor principalAccessor;
public HandlerUsingAccessor(IPrincipalAccessor principalAccessor)
{
this.principalAccessor = principalAccessor;
}
public Task Handle(MyMessage message, IMessageHandlerContext context)
{
var headers = context.MessageHeaders;
var usernameFromHeader = headers["UserName"];
var usernameFromAccessor = principalAccessor?.CurrentPrincipal?.Identity?.Name ?? "null";
log.Info($"Username extracted from header: {usernameFromHeader}");
log.Info($"Username extracted from accessor: {usernameFromAccessor}");
return Task.CompletedTask;
}
}