Управление сеансами в Java – HttpServlet, Cookies, URL Rewriting

Управление сеансом в Java Servlet Веб-приложения – очень интересная тема. Сеанс в Java Servlet управляются различными способами, такими как Cookies, HttpSession API, URL rewriting и т. д. Это третья статья в серии учебников по веб-приложениям на Java, вам может захотеться также ознакомиться с двумя предыдущими статьями.

  1. Учебник по веб-приложениям на Java
  2. Учебник по Java Servlet

Управление сеансом в Java

Эта статья направлена на объяснение управления сеансом в сервлетах с использованием различных техник и примеров программ.

  1. Что такое сеанс?

  2. Управление сеансом в Java – Cookies

  3. Сеанс в Java Servlet – HttpSession

  4. Управление сеансами в Java Servlet – URL-перенаправление

  5. Что такое сессия?

    Протокол HTTP и веб-серверы являются безсостоятельными, что означает, что для веб-сервера каждый запрос является новым запросом для обработки, и он не может определить, идет ли он от клиента, который ранее отправлял запросы. Однако иногда в веб-приложениях нам необходимо знать, кто такой клиент, и обрабатывать запрос соответствующим образом. Например, приложение для корзины покупок должно знать, кто отправляет запрос на добавление товара и в какую корзину товар должен быть добавлен, или кто отправляет запрос на оформление заказа, чтобы можно было списать сумму с правильного клиента. Сессия – это конверсационное состояние между клиентом и сервером, и она может состоять из нескольких запросов и ответов между клиентом и сервером. Поскольку HTTP и веб-сервер оба безсостоятельны, единственный способ поддерживать сессию – передавать между сервером и клиентом некоторую уникальную информацию о сеансе (идентификатор сеанса) в каждом запросе и ответе. Существует несколько способов предоставления уникального идентификатора в запросе и ответе.

    1. Аутентификация пользователя – Это очень распространенный способ, при котором пользователь может предоставить учетные данные аутентификации со страницы входа, а затем мы можем передавать информацию об аутентификации между сервером и клиентом для поддержания сеанса. Этот метод не очень эффективен, потому что он не будет работать, если тот же пользователь вошел в систему из разных браузеров.

    2. Скрытое поле HTML – Мы можем создать уникальное скрытое поле в HTML, и когда пользователь начинает навигацию, мы можем установить его значение уникальным для пользователя и отслеживать сеанс. Этот метод нельзя использовать с ссылками, потому что для этого требуется отправка формы каждый раз, когда клиент отправляет запрос на сервер с этим скрытым полем. Кроме того, это не безопасно, потому что мы можем получить значение скрытого поля из исходного кода HTML и использовать его для взлома сеанса.

    3. Перезапись URL – Мы можем добавить параметр идентификатора сеанса к каждому запросу и ответу, чтобы отслеживать сеанс. Это очень трудоемко, потому что нам нужно отслеживать этот параметр в каждом ответе и убедиться, что он не конфликтует с другими параметрами.

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

    5. API управления сеансом – API управления сеансом построено на основе вышеуказанных методов отслеживания сеанса. Некоторые из основных недостатков всех вышеперечисленных методов:

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

      Вот почему нам нужен API управления сеансом, и технология J2EE Servlet поставляется с API управления сеансом, которое мы можем использовать.

  6. Управление сеансами в 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

  7. Сессия в Java Servlet – HttpSession

    API сервлетов предоставляет управление сессиями через интерфейс HttpSession. Мы можем получить сессию из объекта HttpServletRequest, используя следующие методы. HttpSession позволяет нам устанавливать объекты в качестве атрибутов, которые можно получить в будущих запросах.

    1. HttpSession getSession() – Этот метод всегда возвращает объект HttpSession. Он возвращает объект сеанса, прикрепленный к запросу, если к запросу не прикреплен сеанс, то создает новый сеанс и возвращает его.
    2. HttpSession getSession(boolean flag) – Этот метод возвращает объект HttpSession, если к запросу прикреплена сессия, в противном случае возвращает null.

    Некоторые из важных методов HttpSession:

    1. String getId() – Возвращает строку, содержащую уникальный идентификатор, назначенный этой сессии.
    2. Object getAttribute(String name) – Возвращает объект, связанный с указанным именем в этой сессии, или null, если ни один объект не связан с этим именем. Некоторые другие методы для работы с атрибутами сессии: getAttributeNames(), removeAttribute(String name) и setAttribute(String name, Object value).
    3. long getCreationTime() – Возвращает время создания этой сессии, измеренное в миллисекундах с полуночи 1 января 1970 года GMT. Мы можем получить время последнего доступа с помощью метода getLastAccessedTime().
    4. setMaxInactiveInterval(int interval) – Устанавливает время, в секундах, между запросами клиента, прежде чем контейнер сервлета аннулирует эту сессию. Мы можем получить значение времени ожидания сеанса с помощью метода getMaxInactiveInterval().
    5. ServletContext getServletContext() – Возвращает объект ServletContext для приложения.
    6. boolean isNew() – Возвращает true, если клиент еще не знает о сессии или если клиент выбирает не присоединяться к сессии.
    7. void invalidate() – Аннулирует эту сессию, а затем отменяет привязку к ней всех объектов.

    Когда мы используем метод 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

  8. Управление сеансом в 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();
    }
    }
    %&gt

Вот и все, что касается управления сеансами в Java-сервлетах, мы рассмотрим Фильтры и Слушатели сервлетов и Cookies в следующих статьях. Обновление: Проверьте следующую статью в серии Фильтр сервлета.

Загрузить проекты

Source:
https://www.digitalocean.com/community/tutorials/java-session-management-servlet-httpsession-url-rewriting