Параллельное управление сессиями в Широ - PullRequest
0 голосов
/ 31 января 2019

Я хочу ограничить количество одновременных сессий до 1 на пользователя.Если пользователь входит в систему со второго клиента / IP-адреса, я хотел бы сделать недействительным его предыдущий сеанс (если он есть) и создать новый сеанс для текущего клиента.Поэтому пользователю следует запретить доступ и перенаправить, если он сделает еще один запрос от 1-го клиента.

Я использую Shiro в моем загрузочном приложении Spring.Это чистый API-сервер, а не веб-приложение.Передний и внутренний интерфейсы разделены.

Кажется, что у Широ нет поддержки предела сеанса из коробки.

Мне интересно, где я должен выполнять операции?

В настоящее время у меня есть своя собственная

public class AuthRealm extends AuthorizingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // Get user from db and return authentication info
    }

}

Интересно, это чистое место для добавления соответствующей логики?Или мне следует выполнить предыдущую сессию после входа в систему и создания сессии 2-го клиента?

Я думаю, что было бы более разумно сделать это в

public class AuthRealm extends AuthorizingRealm {

    @Override
    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        // Credentials verified

        // Invalidate previous session
        Subject subject = SecurityUtils.getSubject();
        Session existingSession = subject.getSession(false);
        if (existingSession != null) {
                SecurityUtils.getSecurityManager().logout(subject);
                existingSession.stop();
        }       
    }
}

Но оказывается, что Широ создаетновая тема с новым сеансом, привязанным к нему, для входа в систему, а не для пользователя.Таким образом, subject = SecurityUtils.getSubject() всегда будет совершенно новым, и невозможно получить того же пользователя, а затем получить его сеанс.Есть идеи?

1 Ответ

0 голосов
/ 03 февраля 2019

Проведя много исследований, я обнаружил, что наилучшим способом является написание пользовательского SecurityManager:

    public class UniquePrincipalSecurityManager extends DefaultWebSecurityManager {

        private static final Logger logger = LoggerFactory.getLogger(AuthRealm.class);


        @Override
        public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
            AuthenticationInfo info;

            // Verify credentials
            try {
                info = authenticate(token);
            } catch (AuthenticationException ae) {
                try {
                    onFailedLogin(token, ae, subject);
                } catch (Exception e) {
                    if (logger.isInfoEnabled()) {
                        logger.info("onFailedLogin method threw an " +
                                "exception.  Logging and propagating original AuthenticationException.", e);
                    }
                }
                throw ae; //propagate
            }

            // Check the subject's existing session and stop it if present
            DefaultWebSessionManager sm =  (DefaultWebSessionManager) getSessionManager();
            User loggedInUser = (User)(info.getPrincipals().getPrimaryPrincipal());

            for (Session session : sm.getSessionDAO().getActiveSessions()) {
                SimplePrincipalCollection p = (SimplePrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);

                User sessionUser = null;
                if (p != null) {
                    sessionUser = (User)(p.getPrimaryPrincipal());
                }
                if (sessionUser != null && loggedInUser.getId().equals(sessionUser.getId())) {
                    session.stop();
                    sm.getSessionDAO().delete(session);
                }
            }

            // Create new session for current login
            Subject loggedIn = createSubject(token, info, subject);
            onSuccessfulLogin(token, info, loggedIn);

            return loggedIn;
        }
    }

Обратите внимание, что удалять предыдущий сеанс можно только после проверки учетных данных.

При таком подходе вам необходимо написать собственный менеджер сеансов и sessionDAO, если вы хотите сохранить сеанс в другом хранилище данных.

Обратите внимание, что я использовал Spring Session с redis ранее, но сэтот пользовательский менеджер безопасности, он больше не работает, так как управление сеансом больше не будет делегировано HttpSession.

...