Сессия недействительна при первом запросе

Я использую весеннюю сессию, и мне это очень нравится. Однако я думаю, что что-то упускаю. В моем приложении поток выглядит следующим образом: 1) Пользователь запрашивает HomepageController, и этот контроллер пытается поместить атрибут в запрос:

    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

    final String sessionIds = sessionStrategy.getRequestedSessionId(request);

    if (sessionIds != null) {
        final ExpiringSession session = sessionRepository.getSession(sessionIds);
        if (session != null) {
            session.setAttribute("attr", "value");
            sessionRepository.save(session);
            model.addAttribute("session", session);
        }
    }

Как видите, он попытается получить идентификатор сеанса из запроса-куки, и если в репозитории есть сеанс с этим идентификатором, используйте его (добавьте атрибут). Это идеально, но только после второго запроса. Почему? Потому что если я перезапущу сервер, то кука останется со старым значением, и тогда первый запрос не найдет сессию в репозитории. После подтверждения ответа файл cookie будет обновлен, поэтому второй запрос будет корректным. И вот вопрос: что не так с моей логикой и как нужно развивать приложение, чтобы поддерживать и первый запрос?

Кстати, вот пример приложения, демонстрирующего проблему:

https://github.com/paranoiabla/spring-session-issue


person Petar Tahchiev    schedule 09.04.2015    source источник
comment
почему так сложно? вы можете просто получить сеанс и использовать его. Вся идея в том, что это максимально прозрачно, и вы должны просто использовать HttpSession, а не свою сложную логику.   -  person M. Deinum    schedule 02.05.2015
comment
Потому что, опять же, я работаю с сеансом рядом с db-слоем (у меня есть SessionService, который работает с SessionRepository), и выглядит некрасиво работать с HttpServletSession рядом с db-слоем - это выглядит лучше, когда вы работаете с SessionRepository Session интерфейс.   -  person Petar Tahchiev    schedule 02.05.2015
comment
ИМХО не должны. Вы заявляете, что этот код находится в контроллере и связан с сетью. Вы должны работать только с HttpSession и помещать туда вещи, сеанс будет сохранен для вас фреймворком. Похоже, вы пытаетесь обойти Spring Session вместо того, чтобы принять его. Я бы даже осмелился сказать, что ваш контроллер слишком сложный.   -  person M. Deinum    schedule 02.05.2015


Ответы (2)


Если вы хотите получить сеанс, вам не следует использовать запрошенный идентификатор сеанса. Запрошенный идентификатор сеанса - это именно то, что запрашивает браузер. Некоторые проблемы с использованием запрошенного сеанса (некоторые из которых вы уже описали):

  • Если вы очистите все свои файлы cookie и сделаете запрос, браузер не запрашивает сеанс.
  • Как вы указали, если хранилище данных перезапущено и не является постоянным, запрошенный идентификатор сеанса недействителен.
  • Если срок действия сеанса истекает, запрошенный сеанс будет недействительным.

Вместо этого вы должны использовать идентификатор сеанса:

final String sessionIds = request.getSession().getId();

Это будет использовать запрошенный идентификатор сеанса, если он действителен, в противном случае он создаст новый сеанс и гарантирует, что сеанс будет записан в ответ (т.е. включен в ответ как файл cookie).

person Rob Winch    schedule 15.04.2015
comment
К сожалению, у меня это не работает. Рабочий процесс выглядит следующим образом: чтобы получить sessionId, я делаю request.getSession().getId() вызовы SessionRepositoryFilter, которые вызывают RedisOperationsSessionRepository, которые создают сеанс, но НЕ сохраняют его в Redis!!!.. Теперь у меня есть sessionIdи если я call sessionRepository.getSession(sessionId); репозиторий возвращает null поэтому вся моя логика выдает исключения. Затем ответ фиксируется, и в какой-то момент сеанс сохраняется в Redis, и мой ВТОРОЙ запрос будет успешным (я получаю идентификатор сеанса, который и я нахожу сеанс с этим идентификатором в репозитории Redis) - person Petar Tahchiev; 17.04.2015

Я бы сказал, что ваш подход неверен, ваш контроллер много делает, и вы должны просто использовать HttpSession, для которого Spring Session обеспечивает поддержку. Вы также не должны помещать сеанс в model imho, поскольку вы должны просто обращаться к HttpSession. Ваше приложение не должно знать о Spring Session.

Ваш контроллер должен выглядеть так

@Controller
public class HomepageController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(HttpSession session) {
        session.setAttribute("attr", "value");
        return "homepage";
    }
}

если вы не хотите форсировать создание сеанса, введите HttpServletRequest и выполните getSession(false) вместо внедрения HttpSession.

Все остальное (сохранение сеанса после обработки запроса и т. д.) будет прозрачно обрабатываться Spring Session.

person M. Deinum    schedule 02.05.2015