Как выполнить проверку подлинности запроса AJAX без сброса тайм-аута котяра сессии?


У меня есть существующее веб-приложение Grails, которое находится в производстве и имеет 30-минутный тайм-аут сеанса. Мы запускаем Tomcat (tcServer).

Когда пользователь аутентифицируется и на определенных страницах я хочу сделать несколько периодических запросов опроса ajax к серверу, которые не продлевают этот 30 - минутный тайм-аут сеанса-так, чтобы наш тайм-аут сеанса не был сорван.

Вопрос похож на Этот без ответа asp.net вопрос , но ни один из ответов там не подойдет и это в области Java/Tomcat.

Как выполнить аутентифицированный запрос AJAX без сброса таймаута сеанса tomcat?

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

2 8

2 ответа:

Я бы пошел с фильтром Grails, который делает что-то подобное тому, что предлагает The-MeLLeR без ненужного цикла через все сессии:

class AjaxTimeoutFilters {

   int sessionTimeout = 30 * 60 * 1000
   private static final String TIMEOUT_KEY = 'TIMEOUT_KEY'

   def filters = {
      all(controller:'*', action:'*') {
         before = {
            if (request.xhr) {
               Long lastAccess = session[TIMEOUT_KEY]
               if (lastAccess == null) {
                  // TODO
                  return false
               }
               if (System.currentTimeMillis() - lastAccess > sessionTimeout) {
                  session.invalidate()
                  // TODO - render response to trigger client redirect
                  return false
               }
            }
            else {
               session[TIMEOUT_KEY] = System.currentTimeMillis()
            }

            true
         }
      }
   }
}

Время ожидания сеанса должно быть введено зависимостью или иным образом синхронизировано со значением в web.XML.

Остаются два вопроса. Один-это случай, когда есть Ajax-запрос, но нет предыдущего не-Ajax-запроса (lastAccess == null). Другой способ-перенаправить браузер на страницу входа или туда, куда вам нужно зайти. когда есть запрос Ajax после 30 минут отсутствия не-Ajax активности. Вы должны были бы передать JSON или какой-то другой ответ, который клиент проверил бы, чтобы узнать, что время ожидания истекло, и сделать перенаправление на стороне клиента.

Нет, это невозможно...

Один из вариантов следующий:

1) Создайте javax.сервлет.Отфильтруйте и сохраните метку времени последнего (не ajax) просмотра страницы в сеансе.

2) Создайте javax.сервлет.http.HttpSessionListener для хранения всех активных сеансов.

3) Используйте фоновый поток для аннулирования всех истекших сеансов.


Пример Кода:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class LastAccessFilter implements Filter, HttpSessionListener {
    private static final Object SYNC_OBJECT = new Object();
    private static final String LAST_ACCESSED = "lastAccessed";
    private boolean backgroundThreadEnabled;

    public void destroy() {
        synchronized (SYNC_OBJECT){
            backgroundThreadEnabled = false;
            SYNC_OBJECT.notifyAll();
        }
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        if (req instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) req;
            if(!isAjax(httpServletRequest)){
                httpServletRequest.getSession().setAttribute(LAST_ACCESSED, System.currentTimeMillis());
            }
        }
        chain.doFilter(req, resp);
    }
    public static boolean isAjax(request) {
       return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }   
    public void init(FilterConfig config) throws ServletException {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (LastAccessFilter.this.backgroundThreadEnabled) {
                    synchronized (SYNC_OBJECT) {
                        try {
                            SYNC_OBJECT.wait(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        if (LastAccessFilter.this.backgroundThreadEnabled) {
                            HttpSession[] sessions;
                            synchronized (activeSessions){
                                sessions = activeSessions.toArray(new HttpSession[activeSessions.size()]);
                            }
                            cleanupInactiveSessions(sessions);
                        }
                    }
                }
            }

            private void cleanupInactiveSessions(HttpSession[] sessions) {
                for (HttpSession session : sessions) {
                    Object lastAccessedObject = session.getAttribute(LAST_ACCESSED);
                    if(lastAccessedObject == null) continue;
                    long lastAccessed = (Long)lastAccessedObject;
                    if(System.currentTimeMillis() > (lastAccessed + 1800000)){//30 Minutes
                        session.invalidate();
                    }
                }
            }
        });

        t.setDaemon(true);
        this.backgroundThreadEnabled = true;
        t.start();
    }

    private final List<HttpSession> activeSessions = new ArrayList<HttpSession>();

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        synchronized (activeSessions) {
            this.activeSessions.add(httpSessionEvent.getSession());
        }
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        synchronized (activeSessions) {
            this.activeSessions.remove(httpSessionEvent.getSession());
        }
    }
}