使用共享凭据的具有多个客户实例的多租户应用程序
我已经在 SO 和其他景点周围搜索过,以寻求有关如何执行此操作的帮助。我看到很多人建议不要这样做,但我有合理且简单的理由来解释客户想要这样做的原因。
所以这就是我想要做的:
我有一个多租户应用程序,它具有一个通用框架,用于管理应用程序的多租户方面,例如数据存储位置等。每个租户都会有“x ” 可以访问租户帐户的用户数量。但是,每个租户可以有多个与其帐户绑定的实例。例如,租户可能有一个生产实例和一个测试或开发实例。每个实例都与隔离的数据库具有一对一的关系。对我来说最有意义的是,我将根据租户信息在框架级别存储登录凭据,而不是在每个单独的实例中,因为这会成倍增加用户名和密码的数量。
设计相当简单,有一个租户对象,其中包含用户集合和实例集合。将有一个额外的交叉引用对象,该对象将授权用户访问特定实例。例如,我可能只允许大多数租户用户访问生产实例,但对于系统管理员来说,他们将拥有对所有实例的完全访问权限。
我挂断的部分是这样的:
跨所有实例存储的用户信息实际上应该只包含用户名和密码等基本信息,因为姓名、电话号码等详细信息都应该驻留在实例特定的数据库。更重要的是,用户在每个实例中都将与另一个实体(无论是员工、个人还是联系人对象)建立一对多的关系。
我的问题变成:
根据我的设计,跨模型(框架、实例)链接对象(用户和员工)的最简单方法是什么?更重要的是,如何在所有实例之间保持数据更改或修改同步,或者我是否应该担心这一点?
我真的很感激任何人可以提供的任何反馈,特别是如果您已经解决了这样的问题或有此类模型的经验。我对现有的设计非常满意,因为它必须以这种方式工作才能满足客户的要求,我需要使技术满足要求。
感谢您的专业知识!
布伦特
I have searched around SO and other sights for help on how I might go about doing this. I see many who recommend not doing it but I have legitimate and simple reasons why customers would want to.
So here is what I am trying to do:
I have a multi-tenant application with a common framework for managing the multi-tenant aspects of the application like data storage locations etc. Each tenant will have “x” number of users that can access the tenant’s account. However, each tenant can have multiple instances tied to their account. For instance, the tenant may have a production instance and a test or dev instance as well. Each instance has a one-to-one relationship to an isolated database. It makes the most sense to me that I would store login credentials at the framework level against the tenant information and not in each individual instance as this would increase the number of usernames and passwords exponentially.
The design is fairly simple, there is a Tenant object which would have a collection of users and a collection of instances. There would be an additional cross-reference object which would authorize users to access specific instances. For instance, I may only allow a majority of the tenant users to access the production instance but for system admins they would have full access to all instances.
The part I am hung up on is this:
The user information stored across all instances should really only contain the basics like username and password because the details like Name, Phone number, etc. should all reside in the instance specific database. More importantly the users will have a one-to-many relationship to another entity in each instance, be it an employee, person or contact object.
My question then becomes:
Based on the design I have what is the easiest way to link the objects (user and employee) across the models (framework, instance)? More important, how do I keep data changes or modifications in sync across all instances, or should I even worry about that?
I would really appreciate any feedback anyone can offer, especially if you have tackled a problem like this already or have experience with this type of model. I’m pretty set on the existing design as it has to work this way to meet customer requirements, I need to make the technology work for the requirements.
Thanks for your expertise!
Brent
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
听起来这里似乎存在三个相互关联的事物,尽管问题只围绕一个:
可能有多种方法可以解决每一个问题,但我会这样做:
数据如何存储在数据库中?
由于应用程序的每个租户实例都有一个数据库(每个“环境” - 生产、测试等将是一个租户实例),并且您拥有与所有这些环境相关的数据,我也会将“通用”数据存储在单独的数据库中。
我们将该数据库称为 SystemConfiguration 数据库。其中,您将拥有:
将此系统配置数据库拆分为独立的数据库意味着您不必尝试在任何地方保持公共用户信息同步。如果存在每个实例的用户信息(例如,用户设置或其他信息),则该信息将进入实例数据库,但名称、密码哈希等都将进入该中央数据库。
从技术上讲,您可以通过其他方式对其进行分区,例如也有一个单独的用户数据库,并且仅将租户/系统配置保留在 SystemConfiguration 数据库中,但这至少给了您这个想法。
如果租户决定离开,则可以很容易地查询与特定租户相关的任何信息并将其从 SystemConfiguration 数据库中导出。与实例数据库中特定于租户的数据量相比,不应有很多。
如何确保每个用户只能访问他们有权访问的实例?
我认为基于声明的身份验证/授权将使这个问题变得简单得多。如果您不熟悉声明,其想法是,您拥有与每个用户关联的名称/值对的字典,而不是用户所处的“角色”的静态列表。字典是用户的“声明集”,每个名称/值对都是一个“声明”。相对于角色的好处是该值可以是任何内容,包括列表。
用户在进行身份验证后,将收到一组声明,包括他们的用户名、名字/姓氏以及他们有权访问的实例列表。
之后,只需根据当前实例的 ID 检查用户的声明即可。如果经过身份验证的用户无权访问该实例,请阻止他们。
将其带回数据存储 - 从 SystemConfiguration 数据库生成声明集将是一个简单的查询。
有关基于声明的身份以及如何使用 Windows Identity Foundation 使其在应用程序中运行的更多信息,请查看 Windows Identity Foundation 编程,作者:Vittorio Bertocci。这是一本好书...而且它几乎是唯一一本关于 WIF 的书。
如何将数据映射到代码中使用的对象?
此时,更多的是关于代码中数据的视图,而不是数据存储的结构。如果您希望租户对象具有 IEnumerable其中,这一切都只是与您选择的 ORM 相关。
我可能建议使用服务契约来包装操作,至少围绕 SystemConfiguration 数据库,这样,如果您更改下面的数据,您可以在服务层而不是您的消费代码上调整 ORM。接口!
无论如何,这里的要点是,我不会尝试将数据存储必然基于您使用数据的方式。
最后,如果您的实例中需要用户或租户信息,您最终将有一些查询与 SystemConfiguration 数据库对话,还有一些查询与租户实例数据库对话。没关系。只要您对所有内容使用全局唯一标识符,您就可以仅通过 ID 将一个数据库中的用户与另一个数据库中的某些设置或值联系起来。维护引用完整性有点困难,但它比维护一堆实例数据库之间的数据同步更容易。
这可能是将事物包装在服务中的另一个原因。例如,如果您有“UserSettings”服务,您的合约可能会要求提供租户实例 ID 和用户 ID,并返回设置列表。在幕后,该服务可以从 SystemConfiguration 查询数据,将其映射到适当的实例数据库,并返回合并的数据集 - 所有这些对调用代码都是透明的。另一方面,当您只是更改对象上的值并想要提交更改时,您无法获得某些 ORM 提供的那种不错的“Update()”方法。这是一个权衡,你是否想要对 ORM 进行人为处理以使其符合要求以实现这一目标。
It sounds like there are sort of three interconnected things here, even though the question revolves around just one:
There are probably several ways to solve each one, but here's what I'd do:
How does the data get stored in the database?
Since you have one database per tenant-instance of the application (each "environment" - production, test, etc. would be a tenant-instance) and you have data that pertains to all of them, I would store the "common" data in a separate database, too.
Let's call that database the SystemConfiguration database. In that, you'd have:
Splitting this SystemConfiguration database off as its own thing means you don't have to try to keep common user info synchronized anywhere. If there is per-instance user info (e.g., user settings or something), that'd go in the instance database, but name, password hash, etc. all goes in that central database.
Technically you could partition this in other ways, like having a separate user database, too, and only keeping tenant/system config in the SystemConfiguration database, but this at least gives you the idea.
If a tenant decides to leave, it would be pretty easy to query out any information related to the specific tenant and export it from the SystemConfiguration database. There shouldn't be a lot compared to the amount of tenant-specific data in the instance databases.
How do you ensure each user has access to only the instances to which they have rights?
I think claims-based authentication/authorization would make this problem a lot simpler. If you're not familiar with claims, the idea is that rather than have a static list of "roles" a user is in, you have a dictionary of name/value pairs associated with each user. The dictionary is the user's "claim set" and each name/value pair is a "claim." The benefit over roles is that the value can be anything, including a list.
Users, upon authenticating, would be issued a set of claims including their username, their first/last names, and the list of instances they have access to.
After that, it's a matter of simply checking the user's claims against the current instance's ID. If the authenticated user doesn't have access to that instance, block them.
Bringing it back to the data storage - generating the set of claims from that SystemConfiguration database would be a trivial query.
For more on claims-based identity and how to use Windows Identity Foundation to get that working in your application, check out Programming Windows Identity Foundation by Vittorio Bertocci. It's a good book... and it's pretty much the only book on WIF.
How do you map the data to objects for use in code?
At this point, it's more about the view you want into the data from your code than it is about the structure of the data storage. If you want a Tenant object to have an IEnumerable<User> in it, that's all just futzing with the ORM of your choice.
I might suggest wrapping operations, at least around the SystemConfiguration database, with a service contract so if you change the data underneath, you can tweak the ORM at the service tier rather than your consuming code. Interfaces!
Anyway, the point here is that I'd not try to base the data storage necessarily on the way you consume the data.
Finally, if you need user or tenant information in your instances, you'll end up having some queries talk to the SystemConfiguration database and some talk to the tenant-instance database. That's OK. As long as you use a globally-unique identifier for everything, you can tie a user from the one database to some setting or value in another, just by ID. Referential integrity is a little harder to maintain, but it's easier than maintaining data synchronization across a bunch of instance databases.
This may be another reason to wrap things in services. For example, if you have a "UserSettings" service, your contract might ask for the tenant-instance ID and the user ID, and return the list of settings. Behind the scenes, the service could query data from SystemConfiguration, map it to the appropriate instance database, and return a merged dataset - all transparent to the calling code. On the other hand, you don't get that nice "Update()" method that some ORMs give you when you just change values on the objects and want to commit the changes. It's a trade-off on whether you want to manhandle the ORM into compliance to get that to happen.
重写:
[看起来我原来的答案偏离了目标,而且这大致就是你所拥有的。所以...]
您是否有要求保持它们或任何其他驱动程序同步?如果不...
假设您这样做,您需要决定要同步的对象;唯一的事实来源是什么?
发布/订阅 或 观察者 类型的方法可能就是您所追求的。
Rewrite:
[Looks like my original answer was off-target, and that it's roughly what you have anyway. So...]
Do you have a requirement that requires you to keep them in sync, or any other driver? If not...
Assuming you do, you need to decide what you're going to synchronize against; what's the single source of truth.
A Publish / Subscribe or Observer type approach might be what you're after.