Подтвердить что ты не робот

Счастье или путаница файлов cookie

Я видел на некоторых сайтах, которые пользователь входил в свои аккаунты, а затем закрыл браузер.

После закрытия и повторного открытия браузера и их учетные записи по-прежнему будут подписаны.

Но некоторые веб-сайты не могут этого делать.

Я запутался, что он считает сессию или cookie?

Если я хочу, чтобы мой сайт был подписан таким образом, мне нужно установить session.setMaxInactiveInterval() или cookie.setMaxAge()?

4b9b3361

Ответ 1

* Этот ответ имеет серьезные недостатки, см. комментарии. *


Ваш вопрос касается отслеживания сеанса.

[ЧАСТЬ 1]: ОБЪЕКТ ОБЪЕКТА

HTTP-запрос обрабатывается отдельно, поэтому, чтобы хранить информацию между каждым запросом (например, информацию о пользователе), объект сеанса должен быть создан на стороне сервера.

Некоторые веб-сайты вообще не нуждаются в сеансе. Веб-сайт, на котором пользователи не могут изменять какой-либо контент, не должен управлять сеансом (например, онлайн-резюме). Вам не понадобятся никакие файлы cookie или сеансы на таком веб-сайте.

Создать сеанс:

В сервлете используйте метод request.getSession(true) из объекта HttpServletRequest для создания нового объекта HttpSession. Обратите внимание: если вы используете request.getSession(false), null будет возвращен, если сеанс еще не создан. Посмотрите на этот ответ для более подробной информации.

Установить/Получить атрибуты:

Цель сеанса - хранить информацию на стороне сервера между каждым запросом. Например, сохраняя имя пользователя:

session.setAttribute("name","MAGLEFF");
// Cast
String name = (String) session.getAttribute("name");

Уничтожить сеанс:

Сессия будет автоматически уничтожена, если будет оставаться неактивным слишком много времени. Посмотрите на этот ответ для более подробной информации. Но вы можете вручную заставить сеанс быть уничтоженным, в случае действия выхода из системы, например:

HttpSession session = request.getSession(true); 
session.invalidate();

[ЧАСТЬ 2]: Итак... присоединяйтесь к темной стороне, у нас есть COOKIES?

Вот куки.

JSESSIONID:

Файл cookie JSESSIONID создается на пользовательском компьютере каждый раз, когда сеанс создается с помощью request.getSession(). Зачем? Поскольку каждый сеанс, созданный на стороне сервера, имеет идентификатор. Вы не можете получить доступ к другому сеансу пользователя, если у вас нет правильного идентификатора. Этот идентификатор хранится в файле cookie JSESSIONID и позволяет пользователю находить его информацию. Посмотрите на этот ответ для более подробной информации!

Когда удаляется JSESSIONID?

JSESSIONID не имеет срока годности: это cookie сеанса. Как и все файлы cookie сеанса, он будет удален при закрытии браузера. Если вы используете базовый механизм JSESSIONID, сеанс станет недоступным после закрытия и повторного открытия браузера, поскольку файл cookie JSESSIONID будет удален.

Обратите внимание, что сеанс недоступен клиенту, но он все еще работает на стороне сервера. Установка MaxInactiveInterval позволяет серверу автоматически аннулировать сеанс, если он слишком долго неактивен.

Зло разрушение JSESSIONID

Просто для удовольствия, однажды я нашел этот код в проекте. Он был использован для аннулирования сеанса, удалив файл cookie JSESSIONID с помощью javascript:

<SCRIPT language="JavaScript" type="text/javascript">

    function delete_cookie( check_name ) {
        // first we'll split this cookie up into name/value pairs
        // note: document.cookie only returns name=value, not the other components
        var a_all_cookies = document.cookie.split( ';' );
        var a_temp_cookie = '';
        var cookie_name = '';
        var cookie_value = '';
        var b_cookie_found = false; // set boolean t/f default f
        // var check_name = 'JSESSIONID';
        var path = null;

        for ( i = 0; i < a_all_cookies.length; i++ )
        {
            // now we'll split apart each name=value pair
            a_temp_cookie = a_all_cookies[i].split( '=' );
            // and trim left/right whitespace while we're at it
            cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
            // alert (cookie_name);

            // if the extracted name matches passed check_name
            if ( cookie_name.indexOf(check_name) > -1 )
            {
                b_cookie_found = true;
                // we need to handle case where cookie has no value but exists (no = sign, that is):
                if ( a_temp_cookie.length > 1 )
                {
                    cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
                    document.cookie = cookie_name + "=" + cookie_value +
                    ";path=/" +
                    ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
                    // alert("cookie deleted " + cookie_name);
                }
            }
            a_temp_cookie = null;
            cookie_name = '';
        }
        return true;
    }
    // DESTROY
    delete_cookie("JSESSIONID");

</SCRIPT>

Посмотрите еще раз на этот ответ. С помощью JavaScript JSESSIONID можно прочитать, изменить, потерять или захватить сессию.

[ЧАСТЬ 3]: СОХРАНЕНИЕ СЕССИИ ПОСЛЕ ЗАКРЫТИЯ ВАШЕГО БРАУЗЕРА

После закрытия и повторного открытия браузера и их учетных записей по-прежнему Вошел. Но некоторые веб-сайты не могут этого сделать. Я смущен, что он считает сессию или cookie??

Это cookie.

Мы увидели, что когда куки файл сеанса JSESSIONID был удален веб-браузером, объект сеанса на стороне сервера теряется. Нет доступа к нему снова без правильного идентификатора.

Если я хочу, чтобы мой сайт был подписан таким образом, мне нужно установить session.setMaxInactiveInterval() или cookie.setMaxAge()?

Мы также видели, что session.setMaxInactiveInterval() заключалось в том, чтобы предотвратить бесконечную работу потерянного сеанса. JSESSIONID cookie cookie.setMaxAge() также не получит нас.

Использовать постоянный файл cookie с идентификатором сеанса:

Я пришел к этому решению после прочтения следующих тем:

Основная идея - зарегистрировать сеанс пользователя на карте, помещенный в контекст сервлета. Каждый раз, когда создается сеанс, он добавляется к Карте со значением JSESSIONID для ключа; Постоянный файл cookie также создается для запоминания значения JSESSIONID, чтобы найти сеанс после уничтожения файла cookie JSESSIONID.

Когда вы закрываете веб-браузер, JSESSIONID уничтожается. Но все адреса объектов HttpSession хранятся в карте на стороне сервера, и вы можете получить доступ к правильному сеансу со значением, сохраненным в постоянном файле cookie.

Сначала добавьте двух слушателей в дескриптор развертывания web.xml.

<listener>
    <listener-class>
        fr.hbonjour.strutsapp.listeners.CustomServletContextListener
    </listener-class>
</listener>

<listener>
    <listener-class>
        fr.hbonjour.strutsapp.listeners.CustomHttpSessionListener
    </listener-class>
</listener>

CustomServletContextListener создает карту при инициализации контекста. Эта карта зарегистрирует все сеансы, созданные пользователем в этом приложении.

/**
 * Instanciates a HashMap for holding references to session objects, and
 * binds it to context scope.
 * Also instanciates the mock database (UserDB) and binds it to 
 * context scope.
 * @author Ben Souther; [email protected]
 * @since Sun May  8 18:57:10 EDT 2005
 */
public class CustomServletContextListener implements ServletContextListener{

    public void contextInitialized(ServletContextEvent event){
        ServletContext context = event.getServletContext();

        //
        // instanciate a map to store references to all the active
        // sessions and bind it to context scope.
        //
        HashMap activeUsers = new HashMap();
        context.setAttribute("activeUsers", activeUsers);
    }

    /**
     * Needed for the ServletContextListener interface.
     */
    public void contextDestroyed(ServletContextEvent event){
        // To overcome the problem with losing the session references
        // during server restarts, put code here to serialize the
        // activeUsers HashMap.  Then put code in the contextInitialized
        // method that reads and reloads it if it exists...
    }
}

ПользовательскийHttpSessionListener поместит сессию в карту activeUsers, когда она будет создана.

/**
 * Listens for session events and adds or removes references to 
 * to the context scoped HashMap accordingly.
 * @author Ben Souther; [email protected]
 * @since Sun May  8 18:57:10 EDT 2005
 */
public class CustomHttpSessionListener implements HttpSessionListener{

    public void init(ServletConfig config){
    }

    /**
     * Adds sessions to the context scoped HashMap when they begin.
     */
    public void sessionCreated(HttpSessionEvent event){
        HttpSession    session = event.getSession();
        ServletContext context = session.getServletContext();
        HashMap<String, HttpSession> activeUsers =  (HashMap<String, HttpSession>) context.getAttribute("activeUsers");

        activeUsers.put(session.getId(), session);
        context.setAttribute("activeUsers", activeUsers);
    }

    /**
     * Removes sessions from the context scoped HashMap when they expire
     * or are invalidated.
     */
    public void sessionDestroyed(HttpSessionEvent event){
        HttpSession    session = event.getSession();
        ServletContext context = session.getServletContext();
        HashMap<String, HttpSession> activeUsers = (HashMap<String, HttpSession>)context.getAttribute("activeUsers");
        activeUsers.remove(session.getId());
    }

}

Используйте базовую форму для проверки аутентификации пользователя по имени/паролю. Эта форма login.jsp предназначена только для тестирования.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <title><bean:message key="formulaire1Title" /></title>
    </head>
    <body>
        <form action="login.go" method="get">
            <input type="text" name="username" />
            <input type="password" name="password" />
            <input type="submit" />
        </form>
    </body>
</html>

Там мы идем. Этот сервлет Java пересылает страницу входа, когда пользователь не находится в сеансе, и на другой странице, когда он есть. Он предназначен только для тестирования постоянной сессии!

public class Servlet2 extends AbstractServlet {

    @Override
    protected void doGet(HttpServletRequest pRequest,
            HttpServletResponse pResponse) throws IOException, ServletException {
        String username = (String) pRequest.getParameter("username");
        String password = (String) pRequest.getParameter("password");
        // Session Object
        HttpSession l_session = null;

        String l_sessionCookieId = getCookieValue(pRequest, "JSESSIONID");
        String l_persistentCookieId = getCookieValue(pRequest, "MY_SESSION_COOKIE");

        // If a session cookie has been created
        if (l_sessionCookieId != null)
        {
            // If there isn't already a persistent session cookie
            if (l_persistentCookieId == null)
            {
                addCookie(pResponse, "MY_SESSION_COOKIE", l_sessionCookieId, 1800);
            }
        }
        // If a persistent session cookie has been created
        if (l_persistentCookieId != null)
        {
            HashMap<String, HttpSession> l_activeUsers = (HashMap<String, HttpSession>) pRequest.getServletContext().getAttribute("activeUsers");
            // Get the existing session
            l_session = l_activeUsers.get(l_persistentCookieId);
        }
        // Otherwise a session has not been created
        if (l_session == null)
        {
                    // Create a new session
            l_session = pRequest.getSession();
        }

            //If the user info is in session, move forward to another page
        String forward = "/pages/displayUserInfo.jsp";

        //Get the user
        User user = (User) l_session.getAttribute("user");

        //If there no user
        if (user == null)
        {
                    // Put the user in session
            if (username != null && password != null)
            {
                l_session.setAttribute("user", new User(username, password));
            }
                    // Ask again for proper login
            else
            {
                forward = "/pages/login.jsp";
            }
        }
        //Forward
        this.getServletContext().getRequestDispatcher(forward).forward( pRequest, pResponse );

    }

Файл cookie MY_SESSION_COOKIE сохранит значение cookie JSESSIONID. Когда файл cookie JSESSIONID уничтожен, MY_SESSION_COOKIE все еще существует с идентификатором сеанса.

JSESSIONID ушел с сеансом веб-браузера, но мы решили использовать постоянный и простой файл cookie вместе с картой всех активных сеансов, помещенных в контекст приложения. Постоянный файл cookie позволяет нам найти правильный сеанс на карте.

Не забывайте эти полезные методы, сделанные BalusC для добавления/получения/удаления файлов cookie:

/**
 * 
 * @author BalusC
 */
public static String getCookieValue(HttpServletRequest request, String name) {
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if (name.equals(cookie.getName())) {
                return cookie.getValue();
            }
        }
    }
    return null;
}

/**
 * 
 * @author BalusC
 */
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
    Cookie cookie = new Cookie(name, value);
    cookie.setPath("/");
    cookie.setMaxAge(maxAge);
    response.addCookie(cookie);
}

/**
 * 
 * @author BalusC
 */
public static void removeCookie(HttpServletResponse response, String name) {
    addCookie(response, name, null, 0);
}

}

Последнее решение было протестировано с помощью стеклянной рыбы на локальном хосте, с хромом для веб-браузера, на окнах. Это зависит только от одного файла cookie, и вам не нужна база данных. Но на самом деле, я не знаю, каковы пределы такого механизма. Я только провел ночь, придя к этому решению, не зная, будет ли это хорошо или плохо.

БЛАГОДАРЯ

Я все еще учился, пожалуйста, скажите мне, есть ли какая-либо ошибка в моем ответе. Спасибо, @+

Ответ 2

Правильный ответ имеет много недостатков, см. мой комментарий. Дело на самом деле проще. Вам понадобится постоянный хранилище данных (например, база данных SQL). Вы также можете использовать ServletContext, но пользователь будет выведен из системы после перезапуска сервера или повторного развертывания приложения. Не забудьте правильно синхронизировать, если вы используете HashMap в ServletContext, так как к нему можно одновременно обращаться из большего количества потоков.

Не взламывайте сеанс сервера и его идентификатор, он не находится под вашим контролем, а некоторые серверы меняют идентификатор сеанса, если запрос с JSESSIONID появляется после того, как сервер истек исходный сеанс. Создайте собственный файл cookie.

В принципе вам нужно:

  • собственный файл cookie, который не является постоянным, с безопасно случайным значением
  • хранилище данных
  • a javax.servlet.Filter для проверки входа в систему

Реализация фильтра может выглядеть так:

public class LoginFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // Java 1.8 stream API used here
        Cookie loginCookie = Arrays.stream(req.getCookies()).filter(c -> c.getName()
                .equals("MY_SESSION_COOKIE")).findAny().orElse(null);

        // if we don't have the user already in session, check our cookie MY_SESSION_COOKIE
        if (req.getSession().getAttribute("currentUser") == null) {
            // if the cookie is not present, add it
            if (loginCookie == null) {
                loginCookie = new Cookie("MY_SESSION_COOKIE", UUID.randomUUID().toString());
                // Store that cookie only for our app. You can store it under "/", 
                // if you wish to cover all webapps on the server, but the same datastore
                // needs to be available for all webapps.
                loginCookie.setPath(req.getContextPath());
                loginCookie.setMaxAge(24*60*60); // valid for one day, choose your value
                resp.addCookie(loginCookie);
            }
            // if we have our cookie, check it
            else {
                String userId = datastore.getLoggedUserForToken(loginCookie.getValue());
                // the datastore returned null, if it does not know the token, or 
                // if the token is expired
                req.getSession().setAttribute("currentUser", userId);
            }
        }
        else {
            if (loginCookie != null)
                datastore.updateTokenLastActivity(loginCookie.getValue());
        }

        // if we still don't have the userId, forward to login
        if (req.getSession().getAttribute("currentUser") == null)
            resp.sendRedirect("login.jsp");
        // else return the requested resource
        else
            chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }

}

После входа пользователя в систему вы должны добавить значение MY_SEESSION_COOKIE в хранилище данных вместе с userId и удалить его при выходе из системы. Вы также должны хранить дату истечения срока действия в хранилище данных и проверять ее перед принятием маркера, поскольку браузер может не уважать свойство maxAge, как указано here.

И не забудьте добавить некоторую очистку хранилища данных, чтобы предотвратить выдающиеся файлы cookie навсегда.

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