2010-09-27 3 views
1

Я разрабатываю веб-приложение Java, которое будет работать в защищенной интрасети и не требует входа пользователя. Однако приложение сохраняет диалоговое состояние в HttpSession. Пользовательский ввод не сохраняется в базе данных, пока они явно не нажали кнопку сохранения на каком-то этапе разговора. До тех пор их ввод сохраняется в объекте HttpSession. Если их сеанс истекает, пользователь должен быть перенаправлен на страницу, которая сообщает им о завершении сеанса.Как заставить клиентский браузер останавливать запрос на истекший идентификатор сеанса?

Это прекрасно работает, за исключением проблемы с перенаправлением. Когда пользователь позволяет сеансу сидеть без дела дольше, чем время, указанное в <session-timeout>, сеанс истекает, как ожидалось. Тем не менее, моя попытка перенаправить пользователя на простую страницу «Ваш сеанс истекла», похоже, имеет неприятные последствия. Переадресация работает нормально, но если пользователь не закрывает все открытые окна браузера на своем рабочем столе (а не только те, которые были открыты для моей страницы веб-приложения), они будут постоянно перенаправляться на страницу с истекшим сроком действия.

Вот мои ограничения:

  • Клиентские рабочие станции используют Internet Explorer. Это общеорганизация и не изменится в ближайшее время.
  • У пользователей будут отдельные экземпляры IE на рабочем столе как часть их обычного рабочего процесса. Говорить им закрыть все экземпляры IE не приемлемо.
  • не используя какие-либо компоненты AJAX в этом веб-приложение

Я реализовал редирект с Java Servlet Filter. Вот соответствующие фрагменты кода:

@Override 
public void doFilter(
     ServletRequest request, 
     ServletResponse response, 
     FilterChain filterChain) 
     throws IOException, ServletException { 
    Validate.notNull(filterConfig); 
    Validate.isTrue(request instanceof HttpServletRequest); 
    HttpServletRequest httpServletRequest = (HttpServletRequest) request; 
    String requestedSessionId = httpServletRequest.getRequestedSessionId(); 
    logger.info("requestedSessionId: " + requestedSessionId); 
    HttpSession httpSession = httpServletRequest.getSession(false); 

    if (requestedSessionId == null) { 
     // No need to do anything here if no session exists yet 
     logger.debug("No session exists yet"); 
     filterChain.doFilter(request, response); 
    } else { 
     if (httpSession == null) { 
      Validate.isTrue(response instanceof HttpServletResponse); 
      HttpServletResponse httpServletResponse = 
       (HttpServletResponse) response; 
      handleSessionExpired(
       httpServletRequest, 
       httpServletResponse); 
     } else { 
      if (logger.isDebugEnabled()) { 
       logger.debug("Session OK | requested URL: " + 
        httpServletRequest.getRequestURL().toString()); 
       } 
       filterChain.doFilter(request, response); 
      } 
     } 
    } 
} 

private void handleSessionExpired(
     HttpServletRequest httpServletRequest, 
     HttpServletResponse httpServletResponse) 
     throws IOException { 
    logger.warn("expired session | id: " + 
     httpServletRequest.getRequestedSessionId()); 
    String expirationPageURL = 
     httpServletRequest.getContextPath() + "/" + 
     "SessionExpiredNotification.html"; 
    httpServletResponse.sendRedirect(expirationPageURL); 
} 

SessionExpiredNotification.html страница предназначается, чтобы быть в конце строки. Пользователь должен закрыть это окно браузера и открыть новый, если они хотят начать новый разговор. Проблема в том, что новое окно браузера по-прежнему хочет использовать значение старого идентификатора сеанса, связанное с недействительным сеансом, когда у пользователя есть другие экземпляры Internet Explorer, открытые на рабочем столе. Это не относится к IE, поскольку я подтвердил, что Firefox ведет себя точно так же.

Когда этот код будет достигнут в моем Filter:

String requestedSessionId = httpServletRequest.getRequestedSessionId(); 
logger.info("requestedSessionId: " + requestedSessionId); 

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

Я не уверен, что это актуально, но мой контейнер для веб-приложений Tomcat 6.x.

МОЙ ВОПРОС:
Как веб-сервер приложений сигнализировать рабочей станции клиента, что идентификатор сеанса больше не действует таким образом, что клиент будет отказаться от него?

ответ

0

Это решение, которое я использовал. Я не должен был называть sendRedirect() в моем фильтре, так как это никогда не вернет новый JSESSIONID в клиентский браузер. Мне нужно отправить фактический ответ, который убивает старый JSESSIONID Cookie, иначе браузер-клиент будет продолжать пытаться его использовать. Моя первая мысль заключалась в том, чтобы получить JSESSIONID Cookie из заголовка запроса, установить его истек, а затем включить в файл Cookie с истекшим сроком действия, чтобы клиент действовал по истечении срока действия. Другие пользователи Stackoverflow предположили, что это не чистое решение, поэтому я отказался от этой идеи.

Я заменил метод handleSessionExpired() в Filter, чтобы использовать RequestDispatcher. Это позволит моему Filter отправить запрос на пользовательскую страницу «ваша сессия истек» JSP. В отличие от перенаправления, RequestDispatcher отправит правильный ответ клиенту.

Вот основной метод в моем Filter:

@Override 
public void doFilter(
     ServletRequest request, 
     ServletResponse response, 
     FilterChain filterChain) 
     throws IOException, ServletException { 
    Validate.notNull(filterConfig); 
    Validate.isTrue(request instanceof HttpServletRequest); 
    HttpServletRequest httpServletRequest = 
     (HttpServletRequest) request; 
    String requestedSessionId = httpServletRequest.getRequestedSessionId(); 
    logger.info("requestedSessionId: " + requestedSessionId); 
    HttpSession httpSession = httpServletRequest.getSession(false); 

    if (requestedSessionId == null) { 
     // No need to do anything here if no session exists yet 
     logger.debug("No session exists yet"); 
     filterChain.doFilter(request, response); 
    } else { 
     if (httpSession == null) { 
      Validate.isTrue(response instanceof HttpServletResponse); 
      HttpServletResponse httpServletResponse = 
       (HttpServletResponse) response; 
      handleSessionExpired(
       httpServletRequest, 
       httpServletResponse); 
     } else { 
      filterChain.doFilter(request, response); 
     } 
    } 
} 

handleSessionExpired() Мой метод очень прост. Этот вызов дополнительного метода существует только из-за другого специального варианта использования, который должен обрабатывать мой фильтр (но не имеет отношения к моему исходному вопросу).

private void handleSessionExpired(
     HttpServletRequest httpServletRequest, 
     HttpServletResponse httpServletResponse) 
     throws IOException, ServletException { 
    logger.info("expired session | id: " + 
     httpServletRequest.getRequestedSessionId()); 
    sendSessionExpiredResponse(httpServletRequest, httpServletResponse); 
} 

Мой sendSessionExpiredResponse() также довольно прост. Вызов getSession() приведет к созданию нового сеанса (поскольку на данный момент не существует допустимого HttpSession) и JSESSIONID для включения в ответ. Это позаботится об очистке устаревшего идентификатора сеанса на стороне клиента. Я установил атрибут запроса isExpired, поэтому для завершения сеанса JSP известно, что отображается сообщение о том, что сеанс истек. Я также использую одну и ту же страницу JSP, когда пользователь вручную заканчивает сеанс, поэтому я использую этот атрибут, чтобы определить, какой текст будет отображаться на странице.

private void sendSessionExpiredResponse(
     HttpServletRequest httpServletRequest, 
     HttpServletResponse httpServletResponse) 
     throws IOException, ServletException { 
    httpServletRequest.getSession(true); // force valid session to exist 
    httpServletRequest.setAttribute("isExpired", true); 
    RequestDispatcher rd = filterConfig.getServletContext() 
     .getNamedDispatcher("SessionExpired"); 
    rd.forward(httpServletRequest, httpServletResponse); 
} 

getNamedDispatcher() вызов получает JSP с помощью моего входа в web.xml:

<servlet> 
    <servlet-name>SessionExpired</servlet-name> 
    <jsp-file>/SessionExpired.jsp</jsp-file> 
</servlet> 
2

Если request.getSession(false) возвращает null, тогда вы должны создать новый сеанс. Вы можете сделать это, позвонив по телефону request.getSession(true).

Другими словами, ни в коем случае не указывается, что вы инструктируете контейнер сервлета создать новый сеанс и назначить ему текущий запрос.

+0

мне нужно пользователю, чтобы увидеть простую страницу, которая говорит им, старая сессия истекла и их неспасенный вход был потерян до того они начинают новый сеанс. Если я вызову getSession (true), то разве он не будет продолжать новую беседу без каких-либо указаний на то, что последний был отброшен? –

+0

Нет, это создаст новый сеанс для пользователя, на который будет отвечать контейнер сервлета, отправив обновленному cookie JSESSIONID пользователю. Ваше перенаправление на SessionExpiredNotification.html все равно будет работать. –

+0

@matt_b ОК, я внесу некоторые изменения и попробую по-своему –

Смежные вопросы