【问题标题】:JavaEE + Glassfishv3.0.1: Losing session attribute (SessionId is not the same between request)JavaEE + Glassfishv3.0.1:丢失会话属性(请求之间的SessionId不一样)
【发布时间】:2011-07-06 15:06:52
【问题描述】:

我有一个home.jsf,它调用一个登录servlet,它查看数据库并在给定用户名和密码的情况下查询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");
    }

然后我有一个映射到我所有安全页面的文件管理器,它将尝试从会话中检索用户对象,否则,将我重定向到 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 再次登录。我浏览了一个调试器,所以当我第一次登录时,第一次进入过滤器时,我成功地从request.getSession().getAttribute("user"); 的会话中检索了user 对象。但在那之后,当我回到过滤器中时,我不再使用会话属性user。我在 web.xml 中将会话超时设置为 30 分钟

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

编辑

现在当我打印出请求之间的会话 ID 时,实际上是不同的会话 ID,但我不知道为什么?请帮忙。

EDIT2

@BalusC:实际上我确实使会话无效。那时,您向我展示了如何在用户登录其他地方时强制注销 (http://stackoverflow.com/questions/2372311/jsf-how-to-invalidate-an-user-session-when-he-logs-两次使用相同的凭据)。所以在用户实体里面我有这个

@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 的明显解决方案了吗?

【问题讨论】:

    标签: java session jakarta-ee glassfish glassfish-3


    【解决方案1】:

    HTTP 会话由JSESSIONID cookie 维护。确保您的 Helper.COOKIE_NAME 不使用相同的 cookie 名称,然后它将覆盖会话 cookie。

    如果不是这样,那我不知道。我会使用 Firebug 来调试 HTTP 请求/响应标头。在全新会话的第一个 HTTP 响应中,您应该看到带有会话 ID 的 JSESSIONID cookie 的 Set-Cookie 标头。在同一会话中的所有后续请求中,您应该会看到带有会话 ID 的 JSESSIONID cookie 的 Cookie 标头。

    Cookie 标头不存在或包含JSESSIONID cookie 和(对于服务器端)不存在的会话ID(因为它以某种方式无效)时,或者当服务器具有以具有不同会话 ID 的新 Set-Cookie 标头响应。这应该可以帮助您确定罪魁祸首。是生成新会话 cookie 的服务器吗?还是客户端没有发回会话cookie?

    如果是服务器,那么在服务器端的某个地方会话已经过期/失效。尝试在HttpSession#invalidate() 上设置断点以进一步确定。

    如果是客户端(但这会很奇怪,因为它似乎支持 cookie 很好),然后尝试对重定向 URL 进行编码以包含 JSESSIONID

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

    如有必要,请尝试使用不同的客户,以排除一个和另一个。

    【讨论】:

    • 如何在HttpSession#invalidate()里面打断点,我只看到HttpSession.class,不知道invalidate()的来源在哪里。
    • cookie列表是否可以包含两个同名JSESSIONID但值不同的cookie?当我打印出 cookie 列表时,我所看到的
    • 是的,可以。然而,每个人都应该有不同的路径。你看到了哪些路径?其中之一可能是多余的,并且会干扰您的应用程序的工作。至于断点,不能放在界面上吗? Glassfish 无论如何都是开源的。
    • 因为服务器不对请求中的cookies负责。对此负责的是客户。这就是 HTTP 的设计方式。
    • 有趣。 same 用户因此在 same 会话中被设置了两次。其他东西不是已经预先设置了吗?我想知道为什么getAttribute() 返回null。无论如何,要解决此问题,请按如下方式展开 if 检查:if (session != null &amp;&amp; !session.equals(event.getSession()))
    【解决方案2】:

    查看请求中的 JSessionID 参数。如果它发生变化,则意味着您正在丢失会话(浏览器正在告诉您的服务器它的另一个会话)。不知道为什么会发生,但很可能是您正在做的事情(打开另一个窗口,更改 servlet 上下文并返回,在某些请求中更改服务器......等等)。

    如果您确认,请发布更多信息

    【讨论】:

    • 它实际上是生成不同的会话ID,但我不知道为什么。我不改变窗口。我不认为我正在更改 servlet 上下文,甚至不确定如何更改。绝对不会改变服务器,我运行 GF 作为我的应用程序服务器,我只有 1 台服务器。有什么想法吗?
    • 嗯,你使用 session.invalidate() 吗?或可能会断开您的用户的东西?当您涉及 2 个不同的“应用程序”(战争)时,可能会更改 servlet 上下文……这是您的情况??
    • 我只在注销时使用 session.invalidate。我仔细检查并确保我根本没有调用logout() 方法。我只有包含 EJB 和 WAR 的应用程序 EAR。 GF 怎么样,你能想到什么让 GF 使我的会话无效吗?
    • cookie列表是否可以包含两个同名JSESSIONID但值不同的cookie?当我打印出 cookie 列表时,我所看到的就是
    猜你喜欢
    • 2012-11-28
    • 2015-02-23
    • 1970-01-01
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-12
    相关资源
    最近更新 更多