【问题标题】:Spring Boot WebFlux Reactive MongoDB: how to switch the database on each request?Spring Boot WebFlux Reactive MongoDB:如何在每个请求上切换数据库?
【发布时间】:2021-05-06 11:37:21
【问题描述】:

我正在使用 Spring WebFlux 和 Reactive MongoDB 开发一个 SaaS 项目。 它需要是一个多租户应用程序,并且每个租户都必须使用自己的数据库。

至于现在我只是将 Reactive MongoDB 依赖添加到 pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

然后我扩展了 AbstractReactiveMongoConfiguration 以提供 MongoClient 和 DatabaseName:

import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;

@Configuration
public class DatabaseConfiguration extends AbstractReactiveMongoConfiguration {
    @Override
    public MongoClient reactiveMongoClient() {
        System.out.println("ReactiveMongoClient");
        return MongoClients.create();
    }

    @Override
    protected String getDatabaseName() {
        System.out.println("DataBase name");
        return "gateway";
    }
}

整个应用程序是一个 OAuth 2.0 资源服务器,我能够从 ReactiveSecurityContextHolder 中的 Authentication 检索 TenantID

public Mono<String> tenantID() {
    return ReactiveSecurityContextHolder.getContext()
            .switchIfEmpty(Mono.empty())
            .flatMap((securityContext) -> {
                Authentication authentication = securityContext.getAuthentication();

                if (authentication instanceof CustomAuthenticationToken) {
                    CustomAuthenticationToken customAuthenticationToken = (customAuthenticationToken) authentication;
                    Jwt jwt = customAuthenticationToken.getToken();

                    String issuer = jwt.getIssuer().toString();
                    return Mono.justOrEmpty(issuer);
                }

                return Mono.empty();
            });
}

为了根据执行请求的用户(身份验证)切换数据库,下一步是什么?

更新:

This 几乎接近目标,但差不多一年前@mp911de 说不可能。 想知道现在是否可行。 我怎样才能实现一个真正的反应式 ReactiveMongoDatabaseFactory 返回 Mono 以便我可以在返回 MongoDatabase 之前访问 SecurityContext,从而访问身份验证? p>

【问题讨论】:

  • 不同的数据库是否属于同一个 mongodb 实例/集群并使用相同的数据库用户?还是您需要全新的连接?
  • 对于第一个版本,可以使用相同的 mongodb 实例,但我至少需要使用用户凭据保护每个数据库。

标签: spring-data-mongodb spring-webflux multi-tenant


【解决方案1】:

几个月前我遇到了同样的问题,想分享一下我是如何解决的。

解决方案 1:自己动手

Spring 没有为此问题提供任何开箱即用的解决方案。几个月前,我创建了一个如何解决它的概念证明,并刚刚在 Github 上发布了示例项目。

spring-mongodb-multi-tenancy-example

简而言之:我创建了一个自定义MultiTenancyReactiveMongoTemplate,它基于来自subscriberContexttenantId 在内部委托给实际的ReactiveMongoTemplatetenantId 是从自定义 WebFilter 中的 http-request 标头中提取的,该 WebFilter 将其放入 subscriberContext 中。

此解决方法适用于大多数情况,还支持自动索引创建和使用ReactiveMongoRepository

但也有一些限制,因为MultiTenancyReactiveMongoTemplateReactiveGridFSTemplate 上的事务、IndexOps 不适用于提供的解决方案。有些事情可以用其他委托“模板”来实现,但有些事情永远不会起作用,因为这些操作只是返回标量值(没有Publisher),并且在这些情况下无法访问subscriberContext。

如果您不需要这些功能,您可能会选择此解决方案。

解决方案 2:

您为每个租户/客户启动和配置服务实例,并在这些服务“之前”放置一个反向代理。反向代理决定应该使用哪个服务来处理请求。 反向代理可以很容易地实现,例如 Spring Cloud Gateway,它允许您轻松实现 谓词,这些谓词决定(例如基于身份验证令牌)应该使用哪个服务。

借助 CloudFoundry 或 Kubernetes 等技术,编排这些特定于租户的服务的部署不再那么困难,而且该解决方案还可以轻松地进行特定于租户的监控、警报甚至计费。

我们选择了解决方案 2,因为总体而言这对我们来说更容易并且可以更好地扩展。

【讨论】:

  • 谢谢。我正在寻找第一个解决方案,并通过访问MongoTemplatedoGetDatabase 中的ReactiveSecurityContextHolder 使其工作。也可以通过将 @Configuration 类(每个 MongoTemplate 一个)这个注解 @EnableReactiveMongoRepositories(basePackages = RepositoryLocation.TENANT, reactiveMongoTemplateRef = DatabaseMode.TENANT) 来指定将哪个 MongoTemplate 用于一组存储库
  • 是的,我有相同的解决方案,在 ReactiveMongoTemplate 上覆盖 doGetDatabase,它也很有效,但它需要您配置“默认”MongoClient,这就是我想要避免的。
  • @gmeiner.m 我正在使用您在上面给我们的项目作为多租户的基础,但我正在尝试添加新租户。您能否提供一个线索,如何在您的项目结构中添加新租户?
  • @PaulDev 在我的示例中,可以通过更新配置来简单地添加新租户。看看 application.yml 和 MultiTenancyConfiguration 类。 MultiTenancyConfiguration 配置 WebFilter + MultiTenancyReactiveMongoTemplate。希望这是您正在寻找的答案
  • @gmeiner.m 非常感谢您的回答和指导。我的想法是通过一个休息端点动态添加“newtenant”(如果需要,可以查看:github.com/PauloPortfolio/spring-webflux-mongo-multi-tenancy)。一种简单的多租户。我做的;但是,您的示例工作的“唯一”方式是接受“application.yml 中预定义的租户”。我的目标是通过端点同步添加“新租户”。你能给点提示吗?非常感谢您的示例和提示。
猜你喜欢
  • 2020-12-10
  • 2020-01-29
  • 2022-01-14
  • 2019-10-12
  • 1970-01-01
  • 1970-01-01
  • 2021-04-20
  • 1970-01-01
  • 2019-03-04
相关资源
最近更新 更多