2012-07-16 87 views
2

我正在使用不允许并发登录(同一用户和通行证)的Java EE 6(JSF CDI EJB)的Web应用程序。Java EE 6并发会话和共享

我喜欢的是:

如果需要两次第一届用户登录到无效和旧的会话数据(包括与SessionScope或Apache的CODI其他领域一样WindowScope所有CDI豆)被转移到新的会议。

这有点像通缉会话劫持的做法:-)

+0

为什么不您只隐藏已登录用户的登录页面,并拒绝任何使用类似*的消息进行的登录尝试。如果您希望登录,请登出ogin作为一个不同的用户。“*或者像这样明智的东西。 – BalusC 2012-07-16 21:16:11

+0

它并不那么简单。因为用户希望在不关心登录的情况下在两个不同的工作站上工作。他只是想看到相同的输出和应用程序的相同状态 – urbiwanus 2012-07-16 21:29:38

+0

那么为什么你需要使第一次会话无效? – BalusC 2012-07-16 21:52:48

回答

0

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

公共类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() { 

} 

}

经理看上去很以下

@ApplicationScoped 公共类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 ViewScoped注释豆类

如果第一个你(AJAX)请求会导致会话失效异常,即使使用恢复会话按钮也可轻松处理该异常

只有viewcoped bean的一个小问题是,它们会得到新的视图ID。通过将它们更改回原点,一切正常。

事情,我需要补充:

  • 自动注销(AJAX轮询的WebSockets,...)
  • 某种类型的注册表的所有viewscoped-ID存储

在此评论失踪人员:

  • web.xml配置

问候

+0

我希望看到你回复的任何人都会意识到这只是一个疯狂的内存泄漏(HttpSessions映射永远不会被清除)。 – 2015-03-10 23:39:52

+0

你说得对。此处缺少观察sessionDestroyed事件的HttpSessionListener。侦听器从地图中删除所有无效的会话(即超时,注销,...) – urbiwanus 2015-03-11 07:25:57

0

有是Java EE 6

对此没有内在的机制,我无法想象,你要像一个持续的东西转移从一个会话到另一个会话的用例(例如一个开放的结账过程),我建议你只需要跟踪用户的GUI状态即

RESTful URL听起来像是一个理想的方法。坚持最后一次用户URL /用户操作(例如www.myapp.com/orders/new/12),并重新打开新登录。

如果你不想在数据库中保存它,应用程序范围的映射userid/url可能是KISS的方式。

+0

我不确定这是否适用于我所有的用例。但我会尝试。 – urbiwanus 2012-07-17 08:20:27

+0

出于好奇 - 你正在建造什么样的系统?为什么用户在相对较短的时间内登录到不同的站点? – 2012-07-17 10:48:19

+0

想象一下像不同终端的POS系统。我无法详谈。我很抱歉 – urbiwanus 2012-07-17 18:07:10

0

你可以使用一个无状态的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.) 
}