JavaEE + Glassfishv3.0.1: Losing session attribute

2020-07-27 05:39发布

问题:

I have a home.jsf that invoke a login servlet that look into database and query out the user object given the username and password. Then I save that user object into session under attribute name user, like this 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");
    }

Then I have a Filer that map to all my secured page, that will try to retrieve the user object from the session, otherwise, redirect me to home.jsf to login again

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);
    }

Now as you see here, I manipulate some Cookie as well, but that is only happen when I check remember me. So now I am in CentralFeed.jsf, but then any request that I send from here will bring back to home.jsf to login again. I walk through a debugger, so when I first login, the first time I get into the Filter, i successfully retrieve the user object from session by request.getSession().getAttribute("user");. But after that, when I get back in the filter, I no longer the session attribute user anymore. I set session timeout to be 30 min in my web.xml

<session-config>
    <session-timeout>
        30
    </session-timeout>
</session-config> 

EDIT

Now when I print out the session Id between request, it is fact different session id, but I have no idea why? please help.

EDIT2

@BalusC: I actually did invalidate the session. Back then, you show me how to force a logout when user log in somewhere else (http://stackoverflow.com/questions/2372311/jsf-how-to-invalidate-an-user-session-when-he-logs-twice-with-the-same-credentia). So inside User entity i have this

@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);
   }
}

In the valueBound method, I did invalidate the session, when I comment it out, everything work. I walk through the debugger, and here is what happen. When I first log in, the LoginServlet catch it. Then the line request.getSession().setAttribute("user", user); invoke the method valueBound. Then the Filter got called, and the line chain.doFilter(req, res); invoke the valueBound method again, this time, session is not null so it get in the if and session.invalidate. I comment the session.invalidate out and it work. But as u might have guess, I cant force a log out when user login somewhere else. Do you see a obvious solution for this BalusC?

回答1:

The HTTP session is maintained by the JSESSIONID cookie. Ensure that your Helper.COOKIE_NAME doesn't use the same cookie name, it will then override the session cookie.

If that is not the case, then I don't know. I would use Firebug to debug the HTTP request/response headers. In a first HTTP response on a brand new session you should be seeing the Set-Cookie header with the JSESSIONID cookie with the session ID. In all subsequent requests within the same session, you should be seeing the Cookie header with the JSESSIONID cookie with the session ID.

A new session will be created when the Cookie header is absent or contains a JSESSIONID cookie with a (for the server side) non-existing session ID (because it's been invalidated somehow), or when the server has responded with a new Set-Cookie header with a different session ID. This should help you in nailing down the culprit. Is it the server who generated a new session cookie? Or is it the client who didn't send the session cookie back?

If it was the server, then somewhere in the server side the session has been expired/invalidated. Try putting a breakpoint on HttpSession#invalidate() to nail it further down.

If it was the client (which would be very weird however, since it seems to support cookies fine), then try to encode the redirect URL to include the JSESSIONID.

response.sendRedirect(response.encodeRedirectURL(url));

Try with different clients if necessary to exclude the one and other.



回答2:

look at the JSessionID param in your request. If it changes that means you are losing your session (browser is telling your Server its another session). Dont know why its happening but propably is something you are doing (open another window, change servlet context and come back, change server in some request... etc.).

Please post more information if you confirm that