This sample demonstrates how to attach the current user identity (username) to all outgoing messages and how to extract that value when messages are received. User identity is accessed by a current principal accessor, registered through dependency injection.
This sample doesn't use Thread.
. When used in asynchronous code, Thread.
depends on the version of the .NET runtime. Refer to the Microsoft guidelines for more details.
Fake principal
The sample replaces the principalAccessor.
before sending a message with an ad-hoc value. In a production scenario, the principalAccessor.
would likely be set by the hosting environment e.g. IIS, and not in the user code.
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);
}
await Task.WhenAll(SendMessage(1), SendMessage(2));
The snippet above uses two asynchronous sends to demonstrate that the current principle is properly propagated into the message session.
Custom header with a mutator
The recommended approach for capturing user information is to create a transport mutator extracting the current identity that adds it to the header collection of every outgoing message.
Outgoing message mutator
The outgoing mutator extracts principalAccessor.
and adds it to the message headers collection.
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 sets 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.
The Handler
From within a handler (or saga), the header value holding user identity can be accessed in the following way:
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;
}
}