以线程安全的方式获取服务外部的当前用户会话



基于ServiceStack的应用程序使用内置的身份验证功能,并在SelfHosted模式(AppHostHttpListenerLongRunningBase)下运行。

我使用NHibernate和Envers进行审计跟踪。信封可以提供可选的单例实例,用于填充额外的修订信息。我想将当前用户的身份验证名称存储在修订信息中。

我需要服务代码的外部,在我的singleton实例中访问当前请求(即">当前用户会话")。如何使用ServiceStack?如何使其线程安全?

我自己不使用NHibernate或Envers,所以可能只是在这里吐口水。我不认为当前的用户会话真的存在于服务范围之外。但是你应该能够传入Envers作用域。我认为你想做的是,使用AppHost IoC将对你的单例实例的引用传递到服务中。

AppHost中,设置您的singleton实例,然后将其注册到容器中,以便将其注入到每个Service请求中。

通过做一些类似的事情:

container.Register(c => singletonInstance).ReusedWithin(ReuseScope.None);

您需要扩展Service以使用自定义基础:

public class MyServiceBaseWithEnversSupport : Service
{
public EnversSingletonInstanceType Envers { get; set; } // IoC will inject here
}

然后,您的处理程序将需要使用这个扩展的自定义Service库,因此类似于以下内容:CustomerHandler只是一个示例,您的服务处理程序将有所不同

public class CustomerHandler : MyServiceBaseWithEnversSupport
{
public object Get(ListCustomers request)
{
// You can then access the instance in the scope of the request
// So you now have access to the current user identity
Envers.Username = Session.Username; // Just an example modify as required.
}
}

您可以自动填充值,以避免通过设置自定义ServiceRunner在每个操作处理程序中设置值。

创建自定义ServiceRunner:

public class ServiceRunner<T> : ServiceStack.ServiceHost.ServiceRunner<T>
{
public ServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override object Execute(IRequestContext requestContext, object instance, T request)
{
// Check if the instance is of type MyServiceBaseWithEnversSupport
var ms = instance as MyServiceBaseWithEnversSupport;
// If the request is not using the MyServiceBaseWithEnversSupport, then allow it to run, as normal.
if(ms == null)
return base.Execute(requestContext, instance, request);
// Access the Envers object, set using the Session Information
ms.Envers.Username = ms.Session.Username;
return base.Execute(requestContext, ms, request);
}
}

通过将其添加到AppHost:来配置您的应用程序以使用它

public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext);
}

很抱歉,关于Enver的singleton对象类型以及调用对象以设置数据的正确属性或方法,部分内容有点模糊,但我认为您可以替换适当的值。

正如我所说,我对NHibernate或Envers都不熟悉,所以这可能会被关闭,但至少在ServiceStack方面可能会有所帮助


更新尝试:

我认为,对于您的每一个服务请求,都会发生以下情况:

  • 应用程序主机为请求创建一个服务上下文线程
  • 线程中存在用户会话
  • 每个请求都将执行一些NHibernate数据库操作
  • 数据库操作可能会触发envers侦听器中的事件处理程序。您需要填充当前请求的用户名,即在当前线程的范围内

您是否考虑过在侦听器中创建一个对ServiceStack和Envers事件处理程序都是全局的ThreadStaticstatic变量?

public static class Global
{
[ThreadStatic]
public static string Username;
}

然后在ServiceStack中,在身份验证点设置Username的值,该值将出现在侦听器的处理程序之前。然后在侦听器处理程序中读取Global.Username中的值。该值是线程安全的,并且只存在于请求的范围内。

注意:我假设NHibernate/Envers在请求线程上运行,并且它不会为每个请求生成其他工作线程

最新更新