Управление сеансом в Java Servlet Веб-приложения – очень интересная тема. Сеанс в Java Servlet управляются различными способами, такими как Cookies, HttpSession API, URL rewriting и т. д. Это третья статья в серии учебников по веб-приложениям на Java, вам может захотеться также ознакомиться с двумя предыдущими статьями.
Управление сеансом в Java
Эта статья направлена на объяснение управления сеансом в сервлетах с использованием различных техник и примеров программ.
-
Что такое сессия?
Протокол HTTP и веб-серверы являются безсостоятельными, что означает, что для веб-сервера каждый запрос является новым запросом для обработки, и он не может определить, идет ли он от клиента, который ранее отправлял запросы. Однако иногда в веб-приложениях нам необходимо знать, кто такой клиент, и обрабатывать запрос соответствующим образом. Например, приложение для корзины покупок должно знать, кто отправляет запрос на добавление товара и в какую корзину товар должен быть добавлен, или кто отправляет запрос на оформление заказа, чтобы можно было списать сумму с правильного клиента. Сессия – это конверсационное состояние между клиентом и сервером, и она может состоять из нескольких запросов и ответов между клиентом и сервером. Поскольку HTTP и веб-сервер оба безсостоятельны, единственный способ поддерживать сессию – передавать между сервером и клиентом некоторую уникальную информацию о сеансе (идентификатор сеанса) в каждом запросе и ответе. Существует несколько способов предоставления уникального идентификатора в запросе и ответе.
-
Аутентификация пользователя – Это очень распространенный способ, при котором пользователь может предоставить учетные данные аутентификации со страницы входа, а затем мы можем передавать информацию об аутентификации между сервером и клиентом для поддержания сеанса. Этот метод не очень эффективен, потому что он не будет работать, если тот же пользователь вошел в систему из разных браузеров.
-
Скрытое поле HTML – Мы можем создать уникальное скрытое поле в HTML, и когда пользователь начинает навигацию, мы можем установить его значение уникальным для пользователя и отслеживать сеанс. Этот метод нельзя использовать с ссылками, потому что для этого требуется отправка формы каждый раз, когда клиент отправляет запрос на сервер с этим скрытым полем. Кроме того, это не безопасно, потому что мы можем получить значение скрытого поля из исходного кода HTML и использовать его для взлома сеанса.
-
Перезапись URL – Мы можем добавить параметр идентификатора сеанса к каждому запросу и ответу, чтобы отслеживать сеанс. Это очень трудоемко, потому что нам нужно отслеживать этот параметр в каждом ответе и убедиться, что он не конфликтует с другими параметрами.
-
Куки – Куки – это небольшая часть информации, которая отправляется веб-сервером в заголовке ответа и сохраняется в куки браузера. Когда клиент делает дальнейший запрос, он добавляет куки в заголовок запроса, и мы можем использовать его для отслеживания сеанса. Мы можем поддерживать сеанс с помощью куки, но если клиент отключит куки, то это не сработает.
-
API управления сеансом – API управления сеансом построено на основе вышеуказанных методов отслеживания сеанса. Некоторые из основных недостатков всех вышеперечисленных методов:
- Большую часть времени нам необходимо не только отслеживать сеанс, но и хранить некоторые данные в сеансе, которые мы можем использовать в будущих запросах. Это потребует больших усилий при попытке реализации этого.
- Все вышеперечисленные методы не являются самодостаточными, все они не будут работать в конкретном сценарии. Поэтому нам нужно решение, которое может использовать эти методы отслеживания сеанса для обеспечения управления сеансом во всех случаях.
Вот почему нам нужен API управления сеансом, и технология J2EE Servlet поставляется с API управления сеансом, которое мы можем использовать.
-
-
Управление сеансами в Java – Cookies
Файлы cookie широко используются в веб-приложениях для персонализации ответов на основе ваших предпочтений или для отслеживания сеанса. Прежде чем перейти к API управления сеансами сервлета, я хотел бы показать, как мы можем отслеживать сеанс с помощью файлов cookie через небольшое веб-приложение. Мы создадим динамическое веб-приложение ServletCookieExample с структурой проекта, как на изображении ниже.
Описатель развертывания web.xml веб-приложения:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>ServletCookieExample</display-name> <welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list> </web-app>
Начальная страница нашего приложения – это login.html, где мы получим данные аутентификации от пользователя.
<!DOCTYPE html> <html> <head> <meta charset="US-ASCII"> <title>Страница входа</title> </head> <body> <form action="LoginServlet" method="post"> Имя пользователя: <input type="text" name="user"> <br> Пароль: <input type="password" name="pwd"> <br> <input type="submit" value="Войти"> </form> </body> </html>
Вот LoginServlet, который обрабатывает запрос на вход.
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Реализация сервлета LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "Pankaj"; private final String password = "journaldev"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Получаем параметры запроса для идентификатора пользователя и пароля String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ Cookie loginCookie = new Cookie("user",user); // установка срока действия cookie на 30 минут loginCookie.setMaxAge(30*60); response.addCookie(loginCookie); response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Неправильное имя пользователя или пароль.</font>"); rd.include(request, response); } } }
Обратите внимание на cookie, который мы устанавливаем для ответа, а затем перенаправляем его на LoginSuccess.jsp; этот cookie будет использоваться там для отслеживания сеанса. Также обратите внимание, что время ожидания cookie установлено на 30 минут. В идеале должна быть сложная логика для установки значения cookie для отслеживания сеанса, чтобы он не сталкивался с какими-либо другими запросами.
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Страница успешного входа</title> </head> <body> <% String userName = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); } } if(userName == null) response.sendRedirect("login.html"); %> <h3>Привет, <%=userName %>, успешный вход.</h3> <br> <form action="LogoutServlet" method="post"> <input type="submit" value="Выйти" > </form> </body> </html>
Обратите внимание, что если мы попытаемся получить доступ к JSP напрямую, нас перенаправит на страницу входа. Когда мы нажмем кнопку Выход, мы должны убедиться, что файл cookie удален из браузера клиента.
package com.journaldev.servlet.session;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http -
Сессия в Java Servlet – HttpSession
API сервлетов предоставляет управление сессиями через интерфейс
HttpSession
. Мы можем получить сессию из объекта HttpServletRequest, используя следующие методы. HttpSession позволяет нам устанавливать объекты в качестве атрибутов, которые можно получить в будущих запросах.- HttpSession getSession() – Этот метод всегда возвращает объект HttpSession. Он возвращает объект сеанса, прикрепленный к запросу, если к запросу не прикреплен сеанс, то создает новый сеанс и возвращает его.
- HttpSession getSession(boolean flag) – Этот метод возвращает объект HttpSession, если к запросу прикреплена сессия, в противном случае возвращает null.
Некоторые из важных методов HttpSession:
- String getId() – Возвращает строку, содержащую уникальный идентификатор, назначенный этой сессии.
- Object getAttribute(String name) – Возвращает объект, связанный с указанным именем в этой сессии, или null, если ни один объект не связан с этим именем. Некоторые другие методы для работы с атрибутами сессии:
getAttributeNames()
,removeAttribute(String name)
иsetAttribute(String name, Object value)
. - long getCreationTime() – Возвращает время создания этой сессии, измеренное в миллисекундах с полуночи 1 января 1970 года GMT. Мы можем получить время последнего доступа с помощью метода
getLastAccessedTime()
. - setMaxInactiveInterval(int interval) – Устанавливает время, в секундах, между запросами клиента, прежде чем контейнер сервлета аннулирует эту сессию. Мы можем получить значение времени ожидания сеанса с помощью метода
getMaxInactiveInterval()
. - ServletContext getServletContext() – Возвращает объект ServletContext для приложения.
- boolean isNew() – Возвращает true, если клиент еще не знает о сессии или если клиент выбирает не присоединяться к сессии.
- void invalidate() – Аннулирует эту сессию, а затем отменяет привязку к ней всех объектов.
Понимание куки JSESSIONID
Когда мы используем метод HttpServletRequest getSession() и он создает новый запрос, он создает новый объект HttpSession и также добавляет куки в объект ответа с именем JSESSIONID и значением в качестве идентификатора сеанса. Этот куки используется для идентификации объекта HttpSession в последующих запросах от клиента. Если куки отключены на стороне клиента и мы используем перезапись URL, то этот метод использует значение jsessionid из URL-адреса запроса для поиска соответствующей сессии. Куки JSESSIONID используются для отслеживания сеансов, поэтому мы не должны использовать их для целей нашего приложения, чтобы избежать каких-либо проблем, связанных с сеансами. Давайте рассмотрим пример управления сеансом с использованием объекта HttpSession. Мы создадим динамический веб-проект в Eclipse с контекстом сервлета как ServletHttpSessionExample. Структура проекта будет выглядеть как на изображении ниже.
login.html такой же, как в предыдущем примере, и определен как приветственная страница для приложения в web.xml. Сервлет LoginServlet создаст сеанс и установит атрибуты, которые мы можем использовать в других ресурсах или в будущих запросах.
package com.journaldev.servlet.session;
import java.io.IOException;
import java.io.PrintWriter;import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;/**
* Реализация сервлета класса LoginServlet
*/
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final String userID = "admin";
private final String password = "password";protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {// получение параметров запроса для userID и password
String user = request.getParameter("user");
String pwd = request.getParameter("pwd");if(userID.equals(user) && password.equals(pwd)){
HttpSession session = request.getSession();
session.setAttribute("user", "Pankaj");
// установка срока действия сеанса в 30 минут
session.setMaxInactiveInterval(30 -
Управление сеансом в Java Servlet – Перезапись URL
Как мы видели в последнем разделе, мы можем управлять сеансом с помощью HttpSession, но если отключить файлы cookie в браузере, это не сработает, потому что сервер не получит файл cookie JSESSIONID от клиента. API сервлетов предоставляет поддержку перезаписи URL, которую мы можем использовать для управления сеансом в этом случае. Лучшая часть заключается в том, что с точки зрения кодирования очень легко использовать и включает всего один шаг – кодирование URL. Еще одна хорошая вещь в сервлете URL Encoding заключается в том, что это запасной подход и используется только в том случае, если файлы cookie браузера отключены. Мы можем кодировать URL с помощью метода
encodeURL()
HttpServletResponse, и если нам нужно перенаправить запрос на другой ресурс и мы хотим предоставить информацию о сеансе, мы можем использовать методencodeRedirectURL()
. Мы создадим аналогичный проект, как выше, за исключением того, что мы будем использовать методы перезаписи URL для обеспечения правильной работы управления сеансом, даже если файлы cookie отключены в браузере. Структура проекта ServletSessionURLRewriting в eclipse выглядит как на изображении ниже.пакет com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Реализация сервлета класса LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "admin"; private final String password = "password"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // получение параметров запроса для userID и пароля String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ HttpSession session = request.getSession(); session.setAttribute("user", "Pankaj"); // установка истечения сеанса через 30 минут session.setMaxInactiveInterval(30*60); Cookie userName = new Cookie("user", user); response.addCookie(userName); // получение закодированной строки URL String encodedURL = response.encodeRedirectURL("LoginSuccess.jsp"); response.sendRedirect(encodedURL); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Либо имя пользователя, либо пароль неверны.</font>"); rd.include(request, response); } } }
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Страница успешного входа</title> </head> <body> <% //разрешить доступ только если сеанс существует String user = null; if(session.getAttribute("user") == null){ response.sendRedirect("login.html"); }else user = (String) session.getAttribute("user"); String userName = null; String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); if(cookie.getName().equals("JSESSIONID")) sessionID = cookie.getValue(); } }else{ sessionID = session.getId(); } %> <h3>Привет, <%=userName %>, вход выполнен успешно. Ваш идентификатор сеанса=<%=sessionID %></h3> <br> Пользователь=<%=user %> <br> <!-- необходимо кодировать все URL, где нужна информация о сеансе --> <a href="<%=response.encodeURL("CheckoutPage.jsp") %>">Страница заказа</a> <form action="<%=response.encodeURL("LogoutServlet") %>" method="post"> <input type="submit" value="Выход" > </form> </body> </html>
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Страница успешного входа</title>
</head>
<body>
<%
String userName = null;
//разрешить доступ только если сеанс существует
if(session.getAttribute("user") == null){
response.sendRedirect("login.html");
}else userName = (String) session.getAttribute("user");
String sessionID = null;
Cookie[] cookies = request.getCookies();
if(cookies !=null){
for(Cookie cookie : cookies){
if(cookie.getName().equals("user")) userName = cookie.getValue();
}
}
%>
Вот и все, что касается управления сеансами в Java-сервлетах, мы рассмотрим Фильтры и Слушатели сервлетов и Cookies в следующих статьях. Обновление: Проверьте следующую статью в серии Фильтр сервлета.