2

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?

Thang Pham
  • 38,125
  • 75
  • 201
  • 285

2 Answers2

2

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.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • How do I put a break point inside HttpSession#invalidate(), I only see HttpSession.class, I dont know where the source of invalidate() is. – Thang Pham Mar 01 '11 at 21:09
  • Can cookie list contain two cookie with the same name JSESSIONID but two different value? When I print out the cookie list, that what I see – Thang Pham Mar 01 '11 at 21:50
  • Yes, it can. Each should however have a different path. Which paths do you see? One of them is likely superfluous and disturbing the working of your app. As to the breakpoint, can't you put it on the interface? Glassfish is open source anyway. – BalusC Mar 01 '11 at 22:00
  • Both of the path is `null`. Is this normal? – Thang Pham Mar 01 '11 at 22:05
  • No. Do you see it in request or response? If in request, clear browser cookies and retry. Then check when exactly the server adds the second cookie in response. – BalusC Mar 01 '11 at 22:06
  • just a quick question, when you add cookie, and remove cookie, why did u add it to response instead of request. I am talking about the code that you gave me on this link http://stackoverflow.com/questions/5082846/java-ee-6-how-to-implement-stay-logged-in-when-user-login-in-to-the-web-applic – Thang Pham Mar 01 '11 at 22:11
  • 1
    Because the server is not responsible for the cookies on the request. It's the client who's responsible for that. That's just how HTTP is designed. – BalusC Mar 01 '11 at 22:14
  • oh lord, I finally figure out what happen (just that it break something else). I did invalidate the session myself. I post it in my `Edit2` in my original code. If you have some spare time, please take a look, the cause for something you previously show me (force a logout when user login somewhere else) – Thang Pham Mar 01 '11 at 22:59
  • 1
    Interesting. The *same* user is thus been set twice in the *same* session. Hasn't something else already set it beforehand? I wonder why the `getAttribute()` returned `null`. Anyway, to fix this, expand the `if` check as follows: `if (session != null && !session.equals(event.getSession()))` – BalusC Mar 01 '11 at 23:04
  • Thank you, BalusC. I actually figure why. The same user set valueBound twice. I actually have a SessionBean that does this `Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap(); // sessionMap.put("user", me);` That the reason why, valueBound got set the second time. After I remove now everything work. Thank you very much for staying with me. Thank you – Thang Pham Mar 02 '11 at 00:17
  • Thou this is a very old post, I have been having trouble solving a very similar problem, I have created a new question here [link](http://stackoverflow.com/questions/15829477/why-security-constraint-in-web-xml-is-preventing-redirection), it will be great if you could take a look and provide any direction. Thx. – Murali D Apr 29 '13 at 07:06
1

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

Plínio Pantaleão
  • 1,229
  • 8
  • 13
  • It is in fact generate different session Id, but I am not sure why. I do not change the window. I dont think I am changing servlet context, not even sure how. And definitely not changing the server, I run GF as my application server, and I only have 1 server. Any idea? – Thang Pham Mar 01 '11 at 17:38
  • hummm, do you use session.invalidate()? or something that may disconnect your user? Change servlet context may happens when you have 2 diferent "applications" (wars) involved... Its your case?? – Plínio Pantaleão Mar 01 '11 at 19:50
  • I only use session.invalidate when I log out. I double check and make sure that I did not call `logout()` method at all. I only have application EAR that contain and EJB and WAR. What about GF, can u think of anything that make GF invalidate my session? – Thang Pham Mar 01 '11 at 20:37
  • Can cookie list contain two cookie with the same name JSESSIONID but two different value? When I print out the cookie list, that what I see – Thang Pham Mar 01 '11 at 22:03