Les,
I like the approach. Do you have to be concerned with the realm caching
information though?
Thanks,
Jared
On 07/08/2011 01:51 PM, Les Hazlewood wrote:
> Here's how we do this in our own Shiro-based multi-tenant application:
>
> We have a TenantFilter (a Servlet Filter) that filters all incoming
> HTTP requests (only HTTP/S communication is performed by clients, so
> we only have to worry about the HTTP protocol). The TenantFilter
> inspects the hostname specified in the request (e.g.
> companyname.mysite.com). We parse out 'companyname' and perform a
> lookup in our data store for the Tenant entity corresponding to that
> name.
>
> Once the Tenant object is acquired we bind it to a ThreadLocal so it
> is available for the duration of the request (the TenantFilter ensures
> it is removed from the thread, even in the event of any exception to
> ensure the thread remains 'clean' in a thread-pooled environment).
>
> All tenant-related query logic is handled in our DAO layer - the rest
> of the codebase (including Shiro), never 'knows' about the Tenant
> concept. The DAOs are the ones that inspect the Tenant ThreadLocal
> and manipulate the query.
>
> For example, you might have a UserDAO that looks up a User by
> username, and your Realm implementation would use the UserDAO to
> acquire the User object.
>
> Your Realm's doGetAuthenticationInfo implementation might look
> something like this:
>
> ========================
> UsernamePasswordToken upToken = (UsernamePasswordToken) token;
> String username = upToken.getUsername();
>
> User user = userDAO.findByUsername(username);
> if (user == null) {
> throw new UnknownAccountException("No account found for username
> '" + username + "'");
> } else if (user.isDisabled()) {
> throw new DisabledAccountException("The account for username '" +
> username + "' is disabled");
> } else if (user.isLocked()) {
> throw new LockedAccountException("The account for username '" +
> username + "' is locked");
> }
>
> SimpleAccount shiroAccount = new SimpleAccount(user.getId(),
> user.getHashedPassword(), getName());
> shiroAccount.setCredentialsSalt(new
> SimpleByteSource(Base64.decode(account.getPasswordSalt());
>
> return shiroAccount;
> ========================
>
> Notice how we didn't reference the tenant anywhere?
>
> The UserDAO implementation is the thing that inspects the Tenant
> ThreadLocal. It's 'findByUsername' implementation can do this for
> example (e.g. using JPA or Hibernate queries):
>
> User findByUsername(String username) {
> Object[] queryParams = {username, TENANT_THREAD_LOCAL.get().getId()};
> return executeQuery("from User u where u.username = ? and
> u.tenant.id = ?", queryParams);
> }
>
> We consider the Tenant concept to be a more infrastructural/EIS-level
> concern and not something that should bubble up into code much (if at
> all). The TenantFilter and the DAOs encapsulate this logic quite
> nicely away from the rest of the application.
>
> HTH!
>
> Best regards,
>