JavaEE + Glassfishv3.0.1: Потеря атрибута сеанса (SessionId не совпадает с запросом)
У меня есть home.jsf
, которые вызывают сервлет входа в систему, который ищет базу данных и запрашивает объект user
, учитывая имя пользователя и пароль. Затем я сохраняю этот объект user
в сеансе под именем атрибута user
, как этот request.getSession().setAttribute("user", user);
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
boolean remember = "true".equals(request.getParameter("remember"));
//Hashing the password with SHA-256 algorithms
password = hash(password);
HttpSession s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id: {0}", s.getId());
}
User user = scholarEJB.findUserByUserNamePassword(username, password);
try {
if (user != null) {
request.login(username, password);
request.getSession().setAttribute("user", user);
if (remember) {
String uuid = UUID.randomUUID().toString();
UserCookie uc = new UserCookie(uuid, user.getId());
scholarEJB.persist(uc);
Helper.addCookie(response, Helper.COOKIE_NAME, uuid, Helper.COOKIE_AGE);
}else{
//If the user decide they dont want us to remember them
//anymore, delete any cookie associate with this user off
//the table
scholarEJB.deleteUserCookie(user.getId());
Helper.removeCookie(response, Helper.COOKIE_NAME);
}
response.sendRedirect("CentralFeed.jsf");
}else{
response.sendRedirect("LoginError.jsf");
}
} catch (Exception e) {
response.sendRedirect("LoginError.jsf");
}
Затем у меня есть Filer, который отображает всю мою защищенную страницу, которая попытается извлечь пользовательский объект из сеанса, иначе перенаправите меня на home.jsf
для входа снова
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id Before: {0}", s.getId());
}
User user = (User) request.getSession().getAttribute("user");
s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id After: {0}", s.getId());
}
if (user == null) {
String uuid = Helper.getCookieValue(request, Helper.COOKIE_NAME);
if (uuid != null) {
user = scholarEJB.findUserByUUID(uuid);
if (user != null) {
request.getSession().setAttribute("user", user); //Login
Helper.addCookie(response, Helper.COOKIE_NAME, uuid, Helper.COOKIE_AGE);
} else {
Helper.removeCookie(response, Helper.COOKIE_NAME);
}
}
}
if (user == null) {
response.sendRedirect("home.jsf");
} else {
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(req, res);
}
Теперь, как вы видите здесь, я также манипулирую Cookie, но это происходит только тогда, когда я проверяю remember me
. Итак, теперь я нахожусь в CentralFeed.jsf
, но затем любой запрос, который я отправляю отсюда, вернется к home.jsf
для входа снова. Я прохожу через отладчик, поэтому, когда я впервые вхожу в систему, при первом входе в фильтр я успешно извлекаю объект user
из сеанса на request.getSession().getAttribute("user");
. Но после этого, когда я вернусь в фильтр, я больше не являюсь атрибутом сеанса user
. Я установил тайм-аут сеанса на 30 минут в моем web.xml
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
ИЗМЕНИТЬ
Теперь, когда я распечатываю идентификатор сеанса между запросом, это факт другого идентификатора сеанса, но я понятия не имею, почему? пожалуйста, помогите.
EDIT2
@BalusC: Я действительно сделал недействительным сеанс. Тогда вы показываете мне, как принудительно выйти из системы, когда пользователь заходит в другое место (http://stackoverflow.com/info/2372311/jsf-how-to-invalidate-an-user-session-when-he-logs- два раза-с-же-credentia). Итак, внутри пользовательского объекта я это
@Entity
public class User implements Serializable, HttpSessionBindingListener {
@Transient
private static Map<User, HttpSession> logins = new HashMap<User, HttpSession>();
@Override
public void valueBound(HttpSessionBindingEvent event) {
HttpSession session = logins.remove(this);
if (session != null) {
session.invalidate(); //This is where I invalidate the session
}
logins.put(this, event.getSession());
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
logins.remove(this);
}
}
В методе valueBound
я сделал недействительным сеанс, когда я его прокомментирую, все работает. Я прохожу через отладчик, и вот что происходит. Когда я впервые войду в систему, LoginServlet поймает его. Затем строка request.getSession().setAttribute("user", user);
вызывает метод valueBound
. Затем фильтр получил вызов, а строка chain.doFilter(req, res);
снова вызовет метод valueBound
, на этот раз session
не является нулевым, поэтому он попадает в if и session.invalidate
. Я комментирую session.invalidate и работает. Но, как вы могли бы догадаться, я не могу выйти из системы, когда пользователь заходит в другое место. Вы видите очевидное решение для этого BalusC?
Ответы
Ответ 1
HTTP-сеанс поддерживается файлом JSESSIONID
. Убедитесь, что ваш Helper.COOKIE_NAME
не использует одно и то же имя файла cookie, затем переопределяет файл cookie сеанса.
Если это не так, то я не знаю. Я бы использовал Firebug для отладки заголовков HTTP-запроса/ответа. В первом ответе HTTP на совершенно новом сеансе вы должны увидеть заголовок Set-Cookie
с файлом cookie JSESSIONID
с идентификатором сеанса. Во всех последующих запросах в течение того же сеанса вы должны увидеть заголовок Cookie
с файлом cookie JSESSIONID
с идентификатором сеанса.
Новый сеанс будет создан, если заголовок Cookie
отсутствует или содержит cookie JSESSIONID
с несуществующим идентификатором сеанса (для сервера) (поскольку он был каким-то образом недействителен) или когда сервер имеет ответил новым заголовком Set-Cookie
с другим идентификатором сеанса. Это должно помочь вам справиться с преступником. Это сервер, который создал новый cookie сеанса? Или это клиент, который не отправил cookie сессии обратно?
Если это был сервер, то где-то на стороне сервера сеанс был истек/аннулирован. Попробуйте поставить точку останова на HttpSession#invalidate()
, чтобы прижать ее вниз.
Если это был клиент (это было бы очень странно, поскольку, похоже, оно прекрасно поддерживает файлы cookie), попробуйте закодировать URL-адрес перенаправления, включив в него JSESSIONID
.
response.sendRedirect(response.encodeRedirectURL(url));
Попробуйте с разными клиентами, если необходимо, чтобы исключить тот и другой.
Ответ 2
посмотрите на параметр JSessionID в запросе. Если он изменится, это означает, что вы теряете сеанс (браузер сообщает серверу о своей другой сессии). Не знаю, почему это происходит, но возможно - что-то, что вы делаете (открыть другое окно, изменить контекст сервлета и вернуться, сменить сервер в некотором запросе... и т.д.).
Пожалуйста, напишите дополнительную информацию, если вы подтвердите, что