Многоквартирный дом в Широ

Мы оцениваем Shiro для пользовательского приложения Saas, которое мы создаем. Кажется, что отличный фреймворк делает 90% от того, что мы хотим, из коробки. Мое понимание Сиро является основным, и вот что я пытаюсь достичь.

У нас есть несколько клиентов, каждый с идентичной базой данныхВсе авторизации (роли / разрешения) будут настраиваться клиентами в их собственной выделенной базе данных.Каждый клиент будет иметь уникальный виртуальный хост, например. client1.mycompany.com, client2.mycompany.com и т. д.

Сценарий 1

Authentication done via LDAP (MS Active Directory)
Create unique users in LDAP, make app aware of LDAP users, and have client admins provision them into whatever roles..

Сценарий 2

Authentication also done via JDBC Relam in their database

Вопросов:

Общее для Sc 1 и 2 Как я могу сказать Широ, какую базу данных использовать? Я понимаю, что это должно быть сделано с помощью какого-то специального фильтра аутентификации, но может ли кто-нибудь подсказать мне наиболее логичный способ? Запланируйте использование URL-адреса виртуального хоста, чтобы сообщить сиро и mybatis, какую БД использовать.

Я создаю одну область для каждого клиента?

Sc 1 (Имена пользователей являются уникальными для всех клиентов благодаря LDAP) Если пользователь jdoe совместно используется client1 и client2, и он аутентифицирован через client1 и пытается получить доступ к ресурсу client2, разрешит ли Широ или он снова войдет в систему?

Sc 2 (Имена пользователей уникальны только в базе данных). Если и клиент 1, и клиент 2 создают пользователя с именем jdoe, то сможет ли Широ различать jdoe в клиенте 1 и jdoe в клиенте 2?

Мое решение основано на вкладе Леса ..

public class MultiTenantAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        assertRealmsConfigured();
        TenantAuthenticationToken tat = null;
        Realm tenantRealm = null;

        if (!(authenticationToken instanceof TenantAuthenticationToken)) {
            throw new AuthenticationException("Unrecognized token , not a typeof TenantAuthenticationToken ");
        } else {
            tat = (TenantAuthenticationToken) authenticationToken;
            tenantRealm = lookupRealm(tat.getTenantId());
        }

        return doSingleRealmAuthentication(tenantRealm, tat);

    }

    protected Realm lookupRealm(String clientId) throws AuthenticationException {
        Collection<Realm> realms = getRealms();
        for (Realm realm : realms) {
            if (realm.getName().equalsIgnoreCase(clientId)) {
                return realm;
            }
        }
        throw new AuthenticationException("No realm configured for Client " + clientId);
    }
}

Новый тип токена ..

public final class TenantAuthenticationToken extends UsernamePasswordToken {

       public enum TENANT_LIST {

            CLIENT1, CLIENT2, CLIENT3 
        }
        private String tenantId = null;

        public TenantAuthenticationToken(final String username, final char[] password, String tenantId) {
            setUsername(username);
            setPassword(password);
            setTenantId(tenantId);
        }

        public TenantAuthenticationToken(final String username, final String password, String tenantId) {
            setUsername(username);
            setPassword(password != null ? password.toCharArray() : null);
            setTenantId(tenantId);
        }

        public String getTenantId() {
            return tenantId;
        }

        public void setTenantId(String tenantId) {
            try {
                TENANT_LIST.valueOf(tenantId);
            } catch (IllegalArgumentException ae) {
                throw new UnknownTenantException("Tenant " + tenantId + " is not configured " + ae.getMessage());
            }
            this.tenantId = tenantId;
        }
    }

Изменить мое унаследованное JDBC Realm

public class TenantSaltedJdbcRealm extends JdbcRealm {

    public TenantSaltedJdbcRealm() {
        // Cant seem to set this via beanutils/shiro.ini
        this.saltStyle = SaltStyle.COLUMN;
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return super.supports(token) && (token instanceof TenantAuthenticationToken);
    }

И, наконец, используйте новый токен при входе в систему.

// This value is set via an Intercepting Servlet Filter
String client = (String)request.getAttribute("TENANT_ID");

        if (!currentUser.isAuthenticated()) {
            TenantAuthenticationToken token = new TenantAuthenticationToken(user,pwd,client);
            token.setRememberMe(true);
            try {
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) {
                log.info("The account for username " + token.getPrincipal() + " is locked.  "
                        + "Please contact your administrator to unlock it.");
            } // ... catch more exceptions here (maybe custom ones specific to your application?
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
                ae.printStackTrace();
            }
        }

}

Ответы на вопрос(1)

Ваш ответ на вопрос