使旧的 httpssession 失效会删除当前 httpssession 的上下文

问题描述 投票:0回答:1

我的应用程序使用

@ViewScoped LoginBean
处理登录,其中注入了存储用户信息和当前 HttpSession 的
@SessionScoped SessionBean
。 该应用程序允许用户N单独的会话。 达到该限制后,用户只能通过杀死最旧的来创建另一个。 这是在同一个
LoginBean
中完成的,方法是向非托管
UserSessionManager
询问最旧的
SessionBean
,然后使其
HttpSession
无效。

因此,使用会话“A”登录,我们使会话“B”无效。 这一切都按计划进行。 但是,在剩余的 JSF 阶段中的某个时候,我们也会丢失会话“A”的

SessionBean
。 追踪 CDI 代码,会话“A”的会话上下文似乎正在被销毁,因此当重新显示完成时,我们拥有所有新的会话 bean。

我们正在使用 MyFaces 2.3.6、OpenWebBeans 2.0.16、OpenJDK 11

这是 OWB 中的错误,还是预期的行为?

我也想知道我是否有根本性的误解。如果我将

SessionBean
保存在我的
UserSessionManager
中并在 different 会话期间检索它,它应该保留其原始状态还是在新的
SessionScoped
上下文中重新评估? 我发现调试很困难,因为我的对象似乎实际上是代理,并且 UI 和调试器有时会显示不同的值。

2020 年 4 月 27 日更新:

@SessionScoped SessionBean
正在被 org.apache.webbeans.web.context.WebContextsService#destroyRequestContext() 销毁,它会销毁“PropaatedSessionContext”。 此 PropaatedSessionContext 由 WebContextsService#destroySessionContext() 设置,它指定要销毁的本地会话,尽管给定了不同的特定会话。 这就是我想知道这是否是 OWB 中的错误的地方。

这是代码的简化示例:

(在此测试代码中,我将 SessionManager 设置为 @ApplicationScoped bean。在原始代码中不是,但行为是相同的。)

@Named("loginbean")
@ViewScoped
public class LoginBean implements Serializable {
    private static final long serialVersionUID = 1L;

    private String username;

    @Inject private ExternalContext externalContext;
    @Inject private SessionBean session;
    @Inject private SessionManager sessionMgr;

    public String killOldestDoLogin() {
        List<SessionInfo> sessions = sessionMgr.getSessions();
        SessionInfo oldest = sessions.get(0);
        sessionMgr.killSession(oldest.getSessionId());

        return doLogin();
    }


    public String doLogin() {
        username = username.trim();

        if (username != null && username.length() > 0) {
            // After a successful login, avoid session fixation attacks by
            // rotating the session ID.  This isn't strictly necessary as Faces has
            // its own session ID that a third party wouldn't have access to
            if (externalContext != null) {
                HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
                if (request != null && request.isRequestedSessionIdValid()) {

                    newSessionId   = request.changeSessionId();                    
                }
            }            

            HttpSession http = (HttpSession)externalContext.getSession(false);
            session.setUsername(username);
            session.setHttpSession(http);
            sessionMgr.addSession(http, session);            
        } 

        return "startPage.jsf");
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

.

@Named("sessionbean")
@SessionScoped
public class SessionBean implements Serializable {
    private static final long serialVersionUID = 1L;

    private String username;
    private HttpSession httpSession;

    public void reset() {
        username = null;
        httpSession = null;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public HttpSession getHttpSession() {
        return httpSession;
    }

    public void setHttpSession(HttpSession session) {
        this.httpSession = session;
    }

    public String getSessionId() {
        return httpSession == null ? "null" : this.httpSession.getId();
    }

}

.

@Named("sessionmanager")
@ApplicationScoped
public class SessionManager {
    private HashMap<String,HttpSession> sessionMap = new HashMap<>();
    private HashMap<String,SessionBean> beanMap    = new HashMap<>();

    public void addSession(HttpSession http, SessionBean bean) {
        beanMap.put(http.getId(),  bean);
        sessionMap.put(http.getId(), http);
    }

    public boolean killSession(String sessionId) {
        HttpSession session = sessionMap.get(sessionId);
        sessionMap.remove(sessionId);
        beanMap.remove(sessionId);
        if (session != null) {
            session.invalidate();
        }
        return session != null;
    }

    public List<SessionInfo> getSessions() {
        List<SessionInfo> result = new ArrayList<>();
        for (String sessionId : sessionMap.keySet()) {
            SessionBean bean = beanMap.get(sessionId);
            HttpSession http = sessionMap.get(sessionId);

            SessionInfo info = new SessionInfo();
            info.setUsername(bean.getUsername());
            info.setSessionId(sessionId);
            info.setHttpSession(http));

            result.add(info);
        }

        return result;
    }
}

.

public class SessionInfo {
    private String      username;
    private String      sessionId;
    private HttpSession httpSession;

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSessionId() {
        return sessionId;
    }
    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }
    public HttpSession getHttpSession() {
        return httpSession;
    }
    public void setHttpSession(HttpSession httpSession) {
        this.httpSession = httpSession;
    }
}
jsf servlets cdi httpsession openwebbeans
1个回答
0
投票

我对 Apache TomEE 9.1 (MyFaces 3.0.1) 也有同样的问题。为了消除其他会话,我使用了一种似乎有效的解决方法。我没有直接从 JSF servlet 执行 HttpSession.invalidate(),而是启动一个线程,向其中指示要使哪个会话无效,并且用户的 CDI 会话 bean 不会被破坏。

© www.soinside.com 2019 - 2024. All rights reserved.