Session JavaEE6并发会话和共享

Session JavaEE6并发会话和共享,session,jakarta-ee,cdi,sharing,codi,Session,Jakarta Ee,Cdi,Sharing,Codi,我正在使用Java EE 6 JSF CDI EJB开发一个web应用程序,该应用程序不允许同时登录同一用户和密码 我喜欢的是: 如果用户登录两次,则需要使第一个会话无效,并且旧会话数据(包括所有具有SessionScope或其他作用域(如来自Apache CODI的WindowsScope)的CDI bean)将传输到新会话 这有点像一种被通缉的会话劫持方法:-在JavaEE6中没有这种内在机制 由于我无法想象您想要将某个正在进行的用例(例如,从一个会话到另一个会话的开放签出过程)之类的东西转

我正在使用Java EE 6 JSF CDI EJB开发一个web应用程序,该应用程序不允许同时登录同一用户和密码

我喜欢的是:

如果用户登录两次,则需要使第一个会话无效,并且旧会话数据(包括所有具有SessionScope或其他作用域(如来自Apache CODI的WindowsScope)的CDI bean)将传输到新会话


这有点像一种被通缉的会话劫持方法:-

在JavaEE6中没有这种内在机制

由于我无法想象您想要将某个正在进行的用例(例如,从一个会话到另一个会话的开放签出过程)之类的东西转移到另一个会话,因此我建议您只需跟踪用户的GUI状态

RESTful URL听起来是实现这一点的理想方法。保留最后一个用户URL/用户操作,例如www.myapp.com/orders/new/12,并在新登录时重新打开


如果您不想在数据库中持久化这一点,那么应用程序范围的映射userid/url可能是KISS方式。

在JavaEE6中没有固有的机制

由于我无法想象您想要将某个正在进行的用例(例如,从一个会话到另一个会话的开放签出过程)之类的东西转移到另一个会话,因此我建议您只需跟踪用户的GUI状态

RESTful URL听起来是实现这一点的理想方法。保留最后一个用户URL/用户操作,例如www.myapp.com/orders/new/12,并在新登录时重新打开


如果您不想在DB中保持这种状态,可以使用应用程序范围的映射userid/url。

您可以为用户使用无状态bean,因此每次尝试登录/重新登录时,当前会话都会在登录过程开始时失效

考虑这种方法:

try {           
   session = request.getSession();   //the request is passed by another page or action
   if(session.getAttribute("user") != null) {

           //your code to forward or handle the existing user (re-log in/ do nothing etc.)
}

您可以为用户使用无状态bean,因此每次尝试登录/重新登录时,当前会话都会在登录过程开始时失效

考虑这种方法:

try {           
   session = request.getSession();   //the request is passed by another page or action
   if(session.getAttribute("user") != null) {

           //your code to forward or handle the existing user (re-log in/ do nothing etc.)
}

我在过滤器的帮助下解决了这个问题

公共类SessionReplicationFilter实现筛选器{

@Inject
SessionReplicationManager manager;

public SessionReplicationFilter() {
}


@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    //Process chain first 
    if (chain != null) {
        chain.doFilter(request, response);
    }

    //check http request
    if (request instanceof HttpServletRequest) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        // Retrieve the session and the principal (authenticated user)
        // The principal name is actually the username
        HttpSession session = httpRequest.getSession();
        Principal principal = httpRequest.getUserPrincipal();
        if (principal != null && principal.getName() != null && session != null) {
            manager.checkExistingSession(principal.getName(), session)) 
        }
    }

}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void destroy() {

}
}

经理看起来很像

@适用范围 公共类SessionReplicationManager{

private Map<String, HttpSession> map = new ConcurrentHashMap<String, HttpSession>();


public boolean checkExistingSession(String user, HttpSession session) {
    if (map.keySet().contains(user)) {
        if (!session.getId().equals(map.get(user).getId())) {
            System.out.println("User already logged in ");
            HttpSession oldSession = map.get(user);
            // copies all attributes from the old session to the new session (replicate the session)
            Enumeration<String> enumeration = oldSession.getAttributeNames();
            while (enumeration.hasMoreElements()) {
                String name = enumeration.nextElement();
                System.out.println("Chaning attribut " + name);
                session.setAttribute(name, oldSession.getAttribute(name));
            }
            // invalidates the old user session (this keeps one session per user)
            oldSession.invalidate();
            map.put(user, session);
            return true;
        }
    } else {
        System.out.println("Putting "+user+" into session cache");
        map.put(user, session);
        return false;
    }
    return false;
}
}

它可以很好地与CoDI ViewScope注释bean配合使用

如果第一个用户失效,每个AJAX请求都会导致会话过期异常,即使使用还原会话按钮也可以轻松处理该异常

viewscoped bean唯一的一个小问题是,它们获得了一个新的视图id。通过将它们更改回原始视图id,一切正常

我需要补充的是:

自动注销ajax轮询,WebSocket,。。。 存储所有ViewScope id的某种注册表 此评论中缺少:

web.xml配置
关于

我在过滤器的帮助下解决了这个问题

公共类SessionReplicationFilter实现筛选器{

@Inject
SessionReplicationManager manager;

public SessionReplicationFilter() {
}


@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    //Process chain first 
    if (chain != null) {
        chain.doFilter(request, response);
    }

    //check http request
    if (request instanceof HttpServletRequest) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        // Retrieve the session and the principal (authenticated user)
        // The principal name is actually the username
        HttpSession session = httpRequest.getSession();
        Principal principal = httpRequest.getUserPrincipal();
        if (principal != null && principal.getName() != null && session != null) {
            manager.checkExistingSession(principal.getName(), session)) 
        }
    }

}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void destroy() {

}
}

经理看起来很像

@适用范围 公共类SessionReplicationManager{

private Map<String, HttpSession> map = new ConcurrentHashMap<String, HttpSession>();


public boolean checkExistingSession(String user, HttpSession session) {
    if (map.keySet().contains(user)) {
        if (!session.getId().equals(map.get(user).getId())) {
            System.out.println("User already logged in ");
            HttpSession oldSession = map.get(user);
            // copies all attributes from the old session to the new session (replicate the session)
            Enumeration<String> enumeration = oldSession.getAttributeNames();
            while (enumeration.hasMoreElements()) {
                String name = enumeration.nextElement();
                System.out.println("Chaning attribut " + name);
                session.setAttribute(name, oldSession.getAttribute(name));
            }
            // invalidates the old user session (this keeps one session per user)
            oldSession.invalidate();
            map.put(user, session);
            return true;
        }
    } else {
        System.out.println("Putting "+user+" into session cache");
        map.put(user, session);
        return false;
    }
    return false;
}
}

它可以很好地与CoDI ViewScope注释bean配合使用

如果第一个用户失效,每个AJAX请求都会导致会话过期异常,即使使用还原会话按钮也可以轻松处理该异常

viewscoped bean唯一的一个小问题是,它们获得了一个新的视图id。通过将它们更改回原始视图id,一切正常

我需要补充的是:

自动注销ajax轮询,WebSocket,。。。 存储所有ViewScope id的某种注册表 此评论中缺少:

web.xml配置
关于

您为什么不隐藏已登录用户的登录页面,并以您已登录的消息拒绝任何登录尝试。如果您希望以其他用户身份登录,请注销。或者像这样的明智之举。没那么简单。因为用户希望在两个不同的站点上工作,而不关心登录。他只想看到应用程序的相同输出和相同状态。那么为什么需要使第一个会话无效?第二个会话可能会更改第一个会话上的数据。图像您在第一个会话中打开了订单,然后您更改了工作区,并且您希望在第二个会话中再次编辑订单,但您忘记注销,此时未进行任何更改以保留任何数据。我不想使会话无效,只想将所有数据传输到新创建的oneMyFaces CODI Bean,它存储一个窗口和会话中的所有窗口。如果复制会话的内容,那么也会为所有窗口复制这些bean。为什么不隐藏已登录用户的登录页面,并以您已登录的消息拒绝任何登录尝试。如果您希望以其他用户身份登录,请注销。或者像这样的明智之举。没那么简单。因为用户希望在两个不同的站点上工作,而不关心登录。他只想看到相同的输出和
应用程序的相同状态为什么需要使第一个会话无效?第二个会话可能会更改第一个会话上的数据。图像您在第一个会话中打开了订单,然后您更改了工作区,并且您希望在第二个会话中再次编辑订单,但您忘记注销,此时未进行任何更改以保留任何数据。我不想使会话无效,只想将所有数据传输到新创建的oneMyFaces CODI Bean,它存储一个窗口和会话中的所有窗口。如果您复制会话的内容,那么您也可以为所有窗口复制这些bean。我不确定这是否适用于我的所有用例。但我会尝试一下。只是出于好奇——你正在建立什么样的系统?为什么用户会在相对较短的时间内登录到不同的站点?请想象一个具有不同终端的POS系统。我不能详述。对不起,我不确定这是否能在我所有的用例中都起作用。但我会尝试一下。只是出于好奇——你正在建立什么样的系统?为什么用户会在相对较短的时间内登录到不同的站点?请想象一个具有不同终端的POS系统。我不能详述。我很抱歉,我希望看到你回复的任何人都会意识到这只是一个疯狂的内存泄漏HttpSessions映射永远不会被清除。你是对的。此处缺少HttpSessionListener,它观察sessionDestroyed事件。侦听器从映射中删除所有无效会话,即超时、注销等。我希望看到您回复的任何人都会意识到这只是一个疯狂的内存泄漏HttpSessions映射永远不会被清除。您是对的。此处缺少HttpSessionListener,它观察sessionDestroyed事件。侦听器从映射中删除所有无效会话,即超时、注销等,。。。