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,
--
Les Hazlewood
CTO, Katasoft | http://www.katasoft.com | 888.391.5282
twitter: http://twitter.com/lhazlewood
katasoft blog: http://www.katasoft.com/blogs/lhazlewood
personal blog: http://leshazlewood.com