如何使用 SLIM 处理多租户?
How to handle multi tenancy with SLIM?
我正在使用 SLIM 编写 API
,API 的目标是创建多租户结构。到目前为止我所做的是:
首先,我创建了一个 master 数据库,其中包含我所有的客户(他们购买了我的软件),对于每个客户,我都有一个名为 CUSTOMER_TOKEN
的字段, 表示访问 API.
的凭据
基本上我这里有两个软件:
- API: 从租户数据库读取数据
- 主要应用:使用API。
事实上,每个租户都有不同的数据库,我觉得这种方式对我来说是最好的选择。
主应用程序通过使用 CUSTOMER_TOKEN
获取 JWT
令牌向 API
发送请求,例如:
http://xxx.xxx.xxx.xxx/v1/auth/login
上面的路由将检查 master 数据库中是否存在客户,如果存在,则 return 将使用 API
的临时访问令牌(JWT).
目前一切顺利。主要问题来了。事实上,如果用户获得 JWT
并且 CUSTOMER_TOKEN
有效,那么 API
应该创建到 tenant
数据库的连接而不是 master 数据库.
目前,为了处理主连接,我使用 SLIM
容器,特别是:
<?php
use Slim\Container;
$container = $app->getContainer();
$container['pdo'] = function (Container $c) {
$db = $c['settings']['db'];
$pdo = new PDO(
"mysql:host=" . $db['host'] . ";dbname=" . $db['dbname'],
$db['user'],
$db['pass']
);
return $pdo;
};
如何从此处创建与租户数据库的不同连接?我应该使用 middleware
吗?我是否应该检查请求的路由器以了解我是否需要主数据库或租户数据库?
请注意,对于每个租户,我都使用名称约定结构,例如:
app_name-tenant-tenant_name
所以我的前缀是 app_name-tenant
,然后是 tenant_name
。所以我只需要从 master db 获取 customer
的名称到 assemble 连接。
你会如何处理这种情况?谢谢。
出于安全和实际原因,我建议使用两个不同的 PDO 连接(实例)。第一个连接用于 API (JWT) Auth,第二个数据库连接用于客户(租户)。您不需要中间件,因为基础结构配置的数据库连接部分在中间件之下。
您有多个选项来结合容器管理数据库连接。
- 只需为容器条目添加第二个别名,例如
pdo2
或 db2
或对您有意义的类似名称。
// API
$container['pdo'] = function (Container $container) {
// Customer database
$container['pdo2'] = function (Container $container) {
- 希望您使用constructor dependency injection。如果是,最好使用 class 或接口名称定义容器条目。
示例:
class ApiDatabase extends PDO {}
$container[ApiDatabase::class] = function (Container $container) {
class CustomerDatabase extends PDO {}
$container[CustomerDatabase::class] = function (Container $container) {
name convention
现在您为客户建立了第二个数据库连接,不再需要 table 前缀,因为您已经在技术上更好地分离了客户。为什么?数据库事务也完全分离,这对其他客户数据(和 tables)没有影响。数据库迁移会更容易,因为您不必担心前缀。现在您可以为每个客户部署新版本,而不会影响其他客户的数据库。
我正在使用 SLIM 编写 API
,API 的目标是创建多租户结构。到目前为止我所做的是:
首先,我创建了一个 master 数据库,其中包含我所有的客户(他们购买了我的软件),对于每个客户,我都有一个名为 CUSTOMER_TOKEN
的字段, 表示访问 API.
基本上我这里有两个软件:
- API: 从租户数据库读取数据
- 主要应用:使用API。
事实上,每个租户都有不同的数据库,我觉得这种方式对我来说是最好的选择。
主应用程序通过使用 CUSTOMER_TOKEN
获取 JWT
令牌向 API
发送请求,例如:
http://xxx.xxx.xxx.xxx/v1/auth/login
上面的路由将检查 master 数据库中是否存在客户,如果存在,则 return 将使用 API
的临时访问令牌(JWT).
目前一切顺利。主要问题来了。事实上,如果用户获得 JWT
并且 CUSTOMER_TOKEN
有效,那么 API
应该创建到 tenant
数据库的连接而不是 master 数据库.
目前,为了处理主连接,我使用 SLIM
容器,特别是:
<?php
use Slim\Container;
$container = $app->getContainer();
$container['pdo'] = function (Container $c) {
$db = $c['settings']['db'];
$pdo = new PDO(
"mysql:host=" . $db['host'] . ";dbname=" . $db['dbname'],
$db['user'],
$db['pass']
);
return $pdo;
};
如何从此处创建与租户数据库的不同连接?我应该使用 middleware
吗?我是否应该检查请求的路由器以了解我是否需要主数据库或租户数据库?
请注意,对于每个租户,我都使用名称约定结构,例如:
app_name-tenant-tenant_name
所以我的前缀是 app_name-tenant
,然后是 tenant_name
。所以我只需要从 master db 获取 customer
的名称到 assemble 连接。
你会如何处理这种情况?谢谢。
出于安全和实际原因,我建议使用两个不同的 PDO 连接(实例)。第一个连接用于 API (JWT) Auth,第二个数据库连接用于客户(租户)。您不需要中间件,因为基础结构配置的数据库连接部分在中间件之下。
您有多个选项来结合容器管理数据库连接。
- 只需为容器条目添加第二个别名,例如
pdo2
或db2
或对您有意义的类似名称。
// API
$container['pdo'] = function (Container $container) {
// Customer database
$container['pdo2'] = function (Container $container) {
- 希望您使用constructor dependency injection。如果是,最好使用 class 或接口名称定义容器条目。
示例:
class ApiDatabase extends PDO {}
$container[ApiDatabase::class] = function (Container $container) {
class CustomerDatabase extends PDO {}
$container[CustomerDatabase::class] = function (Container $container) {
name convention
现在您为客户建立了第二个数据库连接,不再需要 table 前缀,因为您已经在技术上更好地分离了客户。为什么?数据库事务也完全分离,这对其他客户数据(和 tables)没有影响。数据库迁移会更容易,因为您不必担心前缀。现在您可以为每个客户部署新版本,而不会影响其他客户的数据库。