Как автоматически выйти из системы с помощью Spring Security

У меня есть веб-приложение Spring, и я выполнил аутентификацию пользователя, используя Spring Security.

Все работает хорошо Войти и Выйти работает отлично!

Теперь я хочу реализовать, чтобы выйти из системы автоматически. Например, если у пользователя есть окно, открытое в течение примерно 30 минут, и ничего не делать (например, истек срок действия сеансов), система должна автоматически выйти из системы. Как я могу это реализовать?

Это может быть реализовано на стороне клиента (я отправляю запросы каждые 1 минуту и ​​проверяю, закончился ли сеанс). Но я не могу сделать это автоматически из весны?

У меня есть этот конфиг:

<http auto-config="true" use-expressions="true">


        <intercept-url pattern="/admin**" />
        <access-denied-handler error-page="/403" />

        <form-login login-page="/login" 
            default-target-url="/admin"
            authentication-failure-url="/login?error" 
            username-parameter="NAME"
            password-parameter="PASSWORD"  />

        <logout invalidate-session="true" 
             logout-success-url="/login?logout"/>

    </http>

и в web.xml

<session-config>
  <session-timeout>1</session-timeout>
</session-config>

через 1 минуту я вижу, что сеанс был уничтожен. убить сеанс через 1 минуту. но страница не была перенаправлена ​​на / login? выход

6 ответов

Решение

Как насчет использования конфигурации безопасности.?? Надеюсь ниже конфиг: будет работать.

applicationContext.xml

 --namespace-> xmlns:security="http://www.springframework.org/schema/security"

        <security:logout invalidate-session="true"
                        success-handler-ref="Logout"
                        logout-url="/logout.html" />
        </security:http>

web.xml

 <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>

И их, вам нужно написать свой собственный, потому что success-handler-ref="Logout" - это собственный обработчик для выхода из системы:
Выйти@Component

public class Logout extends SimpleUrlLogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {

        if (authentication != null) {
            // do something 
        }

        setDefaultTargetUrl("/login");
        super.onLogoutSuccess(request, response, authentication);       
    }
}

Либо это может быть spring-security, spring-mvc или сервлет, автоматический выход из системы невозможен без идеальной логики на стороне клиента.
Учитывая, что приложение будет иметь оба типа запросов

  • AJAX и
  • отправка формы / перезагрузка страницы

Автоматический выход требует очень продуманной логики. Представляю мою реализацию функциональности автоматического выхода со следующими

Преимущества.


1. Для этого не используются дополнительные вызовы / запросы. с учетом влияния на производительность более 10 тыс. активных пользователей и дополнительных вызовов для автоматического выхода из системы.
2. Однострочная конфигурация с использованием тега.
3. Работает безупречно, даже если пользователь открывает несколько вкладок или несколько окон.
4. Он сообщает вам до 30 секунд аннулирования сеанса, поэтому, если вы заполнили форму, но не отправили ее, вы можете сохранить сеанс (продлить сеанс одним щелчком мыши). Таким образом, пользователь с меньшей вероятностью потеряет несохраненные данные.

Применение


1. Включите сценарий автоматического выхода из системы в требуемые страницы JSP, как указано ниже.

    ....
    </body>
    <jsp:include page="../template/autologout-script.jsp"></jsp:include>
</html>

2. Создайте страницу JSP, autologout-script.jsp и добавьте код ниже. Примечание: редактирование / настройка не требуется

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<script>
$(document).ready(function()
{
    var timeOutTimeInSeconds = ${ timeOutTimeInSeconds }; 
    var showTimerTimeInSeconds= ${ showTimerTimeInSeconds };

    var sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);
    var timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);
    var badgeTimerId;
    window.localStorage.setItem("AjaxRequestFired", new Date());

    function redirectToLoginPage(){
        //location.href =  '<c:url value="/" />'+'${loginPageUrl}';
        window.location.reload();
    }

    $(document).ajaxComplete(function () {
        resetTimer();
    });

    $(window).bind('storage', function (e) {
         if(e.originalEvent.key == "AjaxRequestFired"){
             console.log("Request sent from another tab, hence resetting timer")
             resetTimer();
         }
    });

    function resetTimer()
    {
        showTimerTimeInSeconds= ${ showTimerTimeInSeconds };

        console.log("timeOutTimeInSeconds : "+timeOutTimeInSeconds)
        window.localStorage.setItem("AjaxRequestFired", new Date());

        window.clearInterval(sessionCheckIntervalId);
        sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);

        window.clearInterval(timerDisplayIntervalId);
        timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);

        hideTimer();
    }

    function showTimer()
    {
        $('#sessionTimeRemaining').show();
        $('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
        window.clearInterval(timerDisplayIntervalId);
        badgeTimerId = setInterval(function(){
            $('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
        }, 1000);
    }

    function hideTimer()
    {
        window.clearInterval(badgeTimerId);
        $('#sessionTimeRemaining').hide();
    }
});
</script>

3. Настройте атрибуты сеанса для настройки параметра тайм-аута. Примечание. Настройте его после создания сеанса. Вы можете реализовать метод HttpSessionListener sessionCreated и установить следующую конфигурацию в соответствии с вашими требованиями.

session.setMaxInactiveInterval(300);

session.setAttribute("timeOutTimeInSeconds", 300);
session.setAttribute("showTimerTimeInSeconds", 30);

4. Добавьте ниже html для отображения таймера.
Примечание: его можно переместить на страницу шаблона сценария автоматического выхода, если вы хорошо разбираетесь в CSS. Следовательно, вы можете избежать добавления этого на каждой странице.
Включите bootstrap или добавьте свой собственный CSS.

<span class="badge badge-primary" title="click to keep session alive" id="sessionTimeRemaining" 
    onclick="ajaxSessionRefresh()" style="display:none;">
    <i class="badge badge-danger" id="sessionTimeRemainingBadge" style="float:left">30</i>
     &nbsp; 
     <small>Refresh</small>
     <i class="glyphicon glyphicon-refresh"></i>
</span>

Это все о простой реализации автоматического выхода из системы. Вы можете скачать рабочий пример из моего репозитория github.
Autologout с использованием простого примера сервлета.
Autologout с использованием примера конфигурации java
spring -security. Autologout с использованием примера конфигурации xml-spring-security.

Объяснение логики


Случай 1: при загрузке страницы
Здесь логика проста, при загрузке страницы установите таймер интервальных формул на maxInactiveInterval. после тайм-аута перенаправление на страницу входа.
Случай 2. Отслеживайте вызовы AJAX.
Теперь, учитывая запросы AJAX, вы можете использовать обратные вызовы.ajaxStart() или.ajaxComplete() jquery, чтобы при запуске любого запроса ajax вы могли сбросить интервал.
Случай 3: Отслеживание активности нескольких вкладок / окон
Обмен данными между таблицами выполняется для синхронизации состояния каждой вкладки. Используется localStorage при изменении события.

Необходимые ограничения / улучшения
1. Если максимально разрешенный сеанс равен одному, если сеанс взят из другой системы, запрос AJAX завершится ошибкой. Это необходимо для перенаправления на страницу входа.
2. Используйте ajaxStart() вместо ajaxComplete() для точной синхронизации значений idleTime между сервером и браузером.

Требования
1. JQuery

Сравнение альтернатив текущей реализации


1. Установка заголовка Обновить в HTTP-ответе. (Не работает для запросов AJAX)

response.setHeader("Refresh", "60; URL=login.jsp");
  1. Установка мета-тега обновления в HTML (не работает для запросов AJAX)
<meta http-equiv="refresh" content="60; url=login.jsp">
  1. Настройка средства проверки активности Поддерживает сеанс в рабочем состоянии с помощью повторного запроса AJAX. Отслеживает время простоя и делает запрос на выход по истечении тайм-аута.
    Несомненно, это хороший вариант с простой логикой. Но я хочу просто записать свои наблюдения.
    • Влияние на производительность, если в минуту выполняется 2 запроса для поддержания активности сеанса и 50 тыс. Активных пользователей. 100к запросов в минуту.
    • Связь между вкладками Если открыты две вкладки, одна вкладка получает активность, но другая вкладка не получает активности, эта вкладка запускает запрос выхода и аннулирует сеанс, даже если активность присутствует на другой вкладке. (Но с этим можно справиться)
    • Принудительный выход из системы. Клиент доминирует над сервером, чтобы аннулировать сеанс.

Вы можете использовать глобальное значение тайм-аута, поместив его в ваш web.xml:

<session-config>
  <session-timeout>30</session-timeout>
</session-config>

Я пытался найти подходящее решение для той же проблемы с использованием JavaScript и Spring Boot, и это работает для меня, так как простое использование метода expiredUrl("/login?expired") не перенаправляет меня на страницу входа в систему с сообщением об истечении срока действия сеанса.

  1. Создайте функцию JavaScript, которая будет проверять, активен пользователь или нет, и вызовите ее при загрузке тела следующим образом:

HTML- файл:

      <body onload="idleLogout();">
</body>

JavaScript:

      var t;
function idleLogout() {
    window.onload = resetTimer;
    window.onmousemove = resetTimer;
    window.onmousedown = resetTimer; // catches touchscreen presses
    window.onclick = resetTimer;     // catches touchpad clicks
    window.onscroll = resetTimer;    // catches scrolling with arrow keys
    window.onkeypress = resetTimer;

    function logout() {
        window.location = '/login?expired';
    }

    function resetTimer() {
        clearTimeout(t);
        t = setTimeout(logout, 1800000);  // time is in milliseconds
    }
}
  1. Через 30 минут вы будете перенаправлены на страницу /login?expired , где будет использован следующий HTML-код:

            <div th:style="${param.expired} ? 'width: 100%' : 'width: 0'" 
        th:text="#{page.login.form.timeout}"
    </div>
    

Вот учебник, который я использовал. Есть java-скрипт и серверный код. Сервер рассчитает, когда истечет время сеанса, и отправит его обратно в виде файла cookie. Затем java-скрипт будет каждые 10 секунд проверять, истек ли он, если это так, то будет window.close (). http://www.javaworld.com/article/2073234/tracking-session-expiration-in-browser.html

Вот как я это реализовал

SessionTimeout.js

/**
 * Monitor the session timeout cookie from Apache and log the user out when expired
 */
"use strict";

var jQuery = require("jquery").noConflict();
var jsCookie = require("js-cookie");

module.exports.registerListener = function() {
    calcOffset();
    checkSession();
};

/**
 * We can't assume the server time and client time are the same
 * so lets calcuate the difference
 */
function calcOffset() {
    var serverTime = jsCookie.get('serverTime');
    serverTime = serverTime==null ? null : Math.abs(serverTime);
    var clientTimeOffset = (new Date()).getTime() - serverTime;
    jsCookie.set('clientTimeOffset', clientTimeOffset);
}

/**
 * Check the sessionExpiry cookie and see if we should send the user to /
 */
function checkSession() {
    var sessionExpiry = Math.abs(jsCookie.get('sessionExpiry'));
    var timeOffset = Math.abs(jsCookie.get('clientTimeOffset'));
    var localTime = (new Date()).getTime();
    if(!sessionExpiry){
        window.console.log("Unknown session sessionExpiry");
        return;
    }
    if (localTime - timeOffset > (sessionExpiry+15000)) { // 15 extra seconds to make sure
        window.location = "/login";
        jsCookie.remove('sessionExpiry');
    } else {
        setTimeout('checkSession()', 10000);
    }
    window.console.log("Session expires in " + ((sessionExpiry+15000) - localTime - timeOffset) + "ms");
}

window.checkSession = checkSession; //Used for recalling via setTimeout

SessionTimeoutCookieFilter.java

public class SessionTimeoutCookieFilter implements Filter {

    private static final Logger LOG = LoggerFactory.getLogger(SessionTimeoutCookieFilter.class);

    @Override
    public void init(FilterConfig config) throws ServletException {
        LOG.info("Initialization SessionTimeoutCookieFilter");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse httpResp = (HttpServletResponse) resp;
        HttpServletRequest httpReq = (HttpServletRequest) req;

        long currTime = System.currentTimeMillis();
        String expiryTime = Long.toString(currTime + httpReq.getSession().getMaxInactiveInterval() * 1000);
        Cookie cookie = new Cookie("serverTime", Long.toString(currTime));
        cookie.setPath("/");
        httpResp.addCookie(cookie);
        if (httpReq.getRemoteUser() != null) {
            cookie = new Cookie("sessionExpiry", expiryTime);
        }
        cookie.setPath("/");
        httpResp.addCookie(cookie);

        filterChain.doFilter(req, resp);
    }

Добавить фильтр

public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
...
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[]{new SessionTimeoutCookieFilter()};
    }
}

Чтобы перенаправить на страницу входа после истечения срока действия сеанса, добавьте тег "invalid-session-url" к вашему бину "session-management" в контексте безопасности:

<session-management invalid-session-url="/error-login">
    ....
</session-management>

В моем случае я перенаправляю на страницу входа в систему с ошибкой, где отображается сообщение об ошибке, и я могу повторно войти в систему. Имейте в виду, по истечении сеанса вы не будете автоматически перенаправлены. Вам нужно нажать на любую часть вашей страницы, и это вызовет перенаправление.

Другие вопросы по тегам