多租户应用程序中的Spring Social,每个租户都有唯一的ConnectionFactory实例

时间:2013-07-05 12:08:27

标签: java spring multi-tenant spring-social

我正在构建一个支持多个租户的Spring Social应用程序。应用程序根据网址识别租户(例如,tenant1.example.com和tenant2.example.com)。我想允许应用程序的用户使用Facebook或Twitter进行连接。我遇到的问题是,每个租户都是一个独特的企业,并拥有自己独特的Facebook& twitter凭据。我认为我的ConnectionFactoryRegistry中不能有多个Facebook ConnectionFactory实例或多个Twitter ConnectionFactory实例。例如,这不起作用:

ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
registry.addConnectionFactory(new FacebookConnectionFactory("tenant1ClientId", "tenant1ClientSecret"));
registry.addConnectionFactory(new FacebookConnectionFactory("tenant2ClientId", "tenant2ClientSecret"));

以上操作无效,因为根据Spring Social文档,定位器返回单个实例。

public interface ConnectionFactoryLocator {
   ConnectionFactory<?> getConnectionFactory(String providerId);
   <A> ConnectionFactory<A> getConnectionFactory(Class<A> apiType);

   Set<String> registeredProviderIds();
}   

我考虑过创建一个ConnectInterceptor并在preConnect()步骤中将相关的租户凭证注入到提供的ConnectionFactory实例中,但我怀疑,由于共享了connectionFactory实例,因此无法在多线程Web应用程序中使用。

所以,我的下一个想法就是为每个租户创建一个唯一的ConnectionFactoryRegistry实例。这解决了上述问题。

然后问题转移到ConnectController(Spring的OAuth舞蹈控制器)。由于应用程序只能有一个控制器,并且控制器只有一个注册表,我如何指示控制器使用基于租户的正确注册表?

构建多租户Spring Social应用程序的正确方法是什么,该应用程序将为每个租户支持唯一的ConnectionFactory实例?理想情况下,我不想在XML中定义ConnectionFactory实例,因为它们是动态的并存储在数据库中。

2 个答案:

答案 0 :(得分:1)

我过去遇到过类似的问题。 Spring Social +多语言网站(example.de,example.fr,...)=&gt;多个Facebook应用程序(因为使用Facebook应用程序,您只能将用户返回到同一个域)。我最终得到了我的自定义版本的ConnectController(实际上它是ProviderSignInController)和一个助手类(我不确定名称,ConnectionSupport或类似的东西),以便能够在运行时选择正确的FacebookConnectionFactory。

答案 1 :(得分:1)

我发现创建一个CustomConnectionFactoryRegistry也可以解决这个问题并解决下游的问题。 Twitter的例子如下。该解决方案依赖于数据存储库来检索缓存的应用程序租户和providerId的oauth凭据。

public class CustomConnectionFactoryRegistry implements ConnectionFactoryLocator {

    @Autowired
    private ConnectServiceRepository connectServiceRepository; // get the credentials for the tenant

    private final Map<Class<?>, String> apiTypeIndex = new HashMap<Class<?>, String>();

    public CustomConnectionFactoryRegistry(){
        apiTypeIndex.put(Twitter.class, "twitter");
    }

    @Override
    public ConnectionFactory<?> getConnectionFactory(String providerId) {
        OAuthApp app = connectServiceFactory.getProviderApp(providerId);
        if (app == null) {
            throw new IllegalArgumentException("No connection factory for service provider '" + providerId + "' is registered");
        }
        OAuth1ConnectionFactory<?> twitterConnectionFactory = new TwitterConnectionFactory(app.getApiKey(),app.getApiSecret());

        return twitterConnectionFactory;        
    }

    @Override
    @SuppressWarnings("unchecked")
    public <A> ConnectionFactory<A> getConnectionFactory(Class<A> apiType) {
        String providerId = apiTypeIndex.get(apiType);
        if (providerId == null) {
            throw new IllegalArgumentException("No connection factory for API [" + apiType.getName() + "] is registered");
        }
        return (ConnectionFactory<A>) getConnectionFactory(providerId);
    }

}