Как выполнить проверку подлинности запроса AJAX без сброса тайм-аута котяра сессии?
У меня есть существующее веб-приложение Grails, которое находится в производстве и имеет 30-минутный тайм-аут сеанса. Мы запускаем Tomcat (tcServer).
Когда пользователь аутентифицируется и на определенных страницах я хочу сделать несколько периодических запросов опроса ajax к серверу, которые не продлевают этот 30 - минутный тайм-аут сеанса-так, чтобы наш тайм-аут сеанса не был сорван.
Вопрос похож на Этот без ответа asp.net вопрос , но ни один из ответов там не подойдет и это в области Java/Tomcat.
Как выполнить аутентифицированный запрос AJAX без сброса таймаута сеанса tomcat?
Существует ли какой-то фильтр или механизм сопоставления url, который я могу использовать, чтобы исключить запросы от продления таймаута сеанса?
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()); } } }