ASP.NET Multi tenant application with tenant specific roles

隐身守侯 提交于 2019-11-29 05:19:40

I don't think you are going to be able to shoehorn multitenancy into any out of the box role provider, so you might as well keep using SqlMembershipProvider (and SqlRoleProvider). Even the newest Microsoft.AspNet.Identity still assumes a vanilla many-to-many between users and roles. What you really need is to add a 3rd column to the primary key of that many-to-many table, which will id your tenant, i.e.:

user: 6
role: 4
tenant: 17

user: 6
role: 9
tenant: 18 (and so on)

...with this, you are able to have users with different privileges for different tenancies, all using the same set of role names.

If you went with option #2, then your [Authorize] attributes would explode. Imagine this:

[Authorize(Roles = "TenantA:Admin", "TenantB:Admin", ...)]
public ActionResult Post(int id, SomeViewModel model) {}

... all of those attributes would have to be written at compile time unless you went with a custom AuthorizeAttribute, which you could do. But even then you are left creating a new set of roles each time you add a tenant to the system, which should not be necessary.

I work on a big multi-tenancy application. We came to the conclusion that it was easier to maintain separate databases per tenant, and have the web application automatically switch database contexts, rather than try and use an over-complicated database schema to model different tenants.

The benefits

  1. Tenant data is compartmentalized by default into different databases
  2. Tenant data can be exported as a database dump for client MI
  3. Database design is vastly simplified

The drawbacks

  1. You have to manage multiple databases - operations challenge
  2. You have to develop database switching code

Implementation using multiple databases

  1. We used a configuration database that has client settings based on an account code. That account code can come from a login screen or you can map subdomain to client code.
  2. When the app starts you load all tenants into cache (containing connection strings)
  3. On every request you have to determine the client and then switch the db context

I have also developed a multi-tenant application that uses a single database. You quite quickly have problems making sure that you don't cross tenant data. Every query needs to include a tenant id filter. The database queries are therefore always slower as a result, although you can index everything you can to try and improve the situation.

With regards to the Membership question, you can install the membership schema into each tenant database.

What doesn't work

The ideal alternative would be to dynamically switch the ApplicationName, but although it seems to work, ApplicationName is not thread safe, therefore this would not be reliable:

Because a single default membership provider instance is used for all of the requests served by an HttpApplication object, you can have multiple requests executing concurrently and attempting to set the ApplicationName property value. The ApplicationName property is not thread safe for multiple writes, and changing the ApplicationName property value can result in unexpected behavior for multiple users of an application. We recommend that you avoid writing code that allows users to set the ApplicationName property, unless you must. An example of an application where setting the ApplicationName property may be required is an administrative application that manages membership data for multiple applications. Such an application should be a single-user application and not a Web application.

Alternative: MembershipReboot

Multi-tenancy is hard in .Net. An open source alternative to using the built in Membership is to use MembershipReboot, written by Brock Allen. It has some excellent features including multi-tenant support out-of-the-box:

  1. single- or multi-tenant account management
  2. flexible account storage design (relational/SQL or object/NoSql), samples using both EF and RavenDB
  3. claims-aware user identities
  4. support for account registration, email verification, password reset, etc.
  5. account lockout for multiple failed login attempts (password guessing)
  6. extensible templating for email notifications
  7. customizable username, password and email validation
  8. notification system for account activity and updates (e.g. for auditing)
  9. account linking with external identity providers (enterprise or social)
  10. supports certificate based authentication
  11. proper password storage (via PBKDF2)
  12. configurable iterations
  13. defaults to OWASP recommendations for iterations (e.g. 64K in year 2012)
  14. two factor authentication support via mobile phone SMS messages or client certificates

The most common use case will be to integrate this into an ASP.NET or ASP.NET MVC application, though the library can also be used over a network as a service.

Alternative: ServiceStack REST

Another alternative if you are building modern web applications that heavily use JavaScript MVC frameworks such as AngularJS, EmberJS or BackboneJS is to use ServiceStack REST services. ServiceStack has a long list of Authentication features, and from my experience of SS, I find it has an extremely well thought out API model.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!