2017-03-09 84 views
1

我想了解如何为涉及多租户的SaaS产品构建RESTful API。技术栈是使用Spring和Hibernate的Java,并将WAR部署到Tomcat。在使用Spring和Hibernate和Tomcat的无状态环境中实现多租户

我的主要问题是我们如何维护REST调用中的tenant_id,以便应用程序在执行CRUD时使用正确的数据库连接。看到Tomcat使用线程池并重用线程,我们不应该使用ThreadLocal。

我读过slf4j支持MDC实现进行日志记录。 servelet过滤器会保留tenant_id,并在过滤器退出时将其清除。因此,记录器在消息中使用正确的tenant_id。

同时使用ThreadLocal违背了无状态原则,因为这隐含地增加了一个状态。

此外,创建某种持有tenant_id并传递它的ContextSession对象的想法似乎不能解决我的问题。因为这个对象将传递给DAL和DAO的层来加载对象。我想避免在这个ContextSession类上的这种高度耦合,以及不得不将它包含在许多方法签名中。

如何在无状态环境中实现多租户?

回答

0

当用户登录到您的应用程序时,它们会以某种方式与租户关联。所以租户信息应该可以从Spring SecurityContext获取。

作为一个例子,如果我有一个应用程序与2租户; tenantA和tenantB。登录时,用户需要指出他们属于哪个租户。这可以通过各种方式完成,例如使用不同的主机名(例如tenantA.myapp.com,tenantB.myapp.com)或url参数,或在登录表单中输入租户信息。在对用户进行身份验证时,您需要使用与特定租户关联的身份验证领域。作为身份验证的一部分,应该设置Spring SecurityContext以包含一些信息,通过该信息可以确定用户在后续服务调用中属于哪个租户。应该可以从应用程序的不同服务层访问SecurityContext,而无需为此发生任何明确的程序设计。

+0

谢谢。所以在DAO中,我将如何为用户的tenant_id注入SecurityContex?我认为其范围将会议? –

+0

查看http://www.baeldung.com/get-user-in-spring-security,了解有关如何访问SecurityContext的想法 – httPants

+0

另外,SecurityContext不一定必须是会话作用域。如果你使用类似oauth2的无状态会话,请求中使用的令牌将存储在securitycontext中并包含领域信息(它将告诉你租户)。 – httPants

0

有多种方法可将您当前的HttpSession与特定租户相关联。

  1. 上,用于反向通过集中式通用客户端数据库查找指定的后端tenant的URL提供某种类型client_id参数。

  2. 将经过身份验证的用户与特定的tenant关联。当用户通过集中用户数据库进行身份验证时,会根据用户帐户查找租户。

tenant_id如何传递到较低的应用程序层是一个真正的品味问题。

我的第一个选择是使用ThreadLocal。如果你已经在使用Spring Security,你已经在使用ThreadLocal变量,甚至可能不知道它。使用ThreadLocal变量不会破坏应用程序的无状态设计。你只是有一段代码需要一个变量来包含它必须使用的特定值。这只是一种在你的应用程序层中传递状态而不实际显式地将它作为参数传递给方法签名的方式。

显然其他两个选择是使用一些上下文对象或仅仅直接传递值。

在Web应用程序中,我通常会通过一些拦截器或过滤器,为每个请求查找请求参数client_id并得到相关tenantId,并设置在一个特殊的ThreadLocal变量做到这一点。

一旦呼叫chain.doFilter(request, response);已返回或引发异常,您只需清除ThreadLocal变量。