Java 当用户使用相同的凭据登录两次时,如何使用户会话无效

Java 当用户使用相同的凭据登录两次时,如何使用户会话无效,java,session,jsf,richfaces,facelets,Java,Session,Jsf,Richfaces,Facelets,我将JSF1.2与Richfaces和Facelets一起使用 我有一个包含许多会话范围bean和一些应用程序bean的应用程序 用户使用Firefox登录。使用ID=“A”创建会话; 然后,他打开Chrome并使用相同的凭据再次登录。使用ID=“B”创建会话 创建会话“B”时,我希望能够销毁会话“A”。怎么做 还有。当Firefox中的用户执行任何操作时,我希望能够显示一个弹出窗口或某种通知,说明“您已注销,因为您已从其他地方登录” 我有一个sessionListener,他跟踪创建和销毁的会

我将JSF1.2与Richfaces和Facelets一起使用

我有一个包含许多会话范围bean和一些应用程序bean的应用程序

用户使用Firefox登录。使用ID=“A”创建会话; 然后,他打开Chrome并使用相同的凭据再次登录。使用ID=“B”创建会话

创建会话“B”时,我希望能够销毁会话“A”。怎么做

还有。当Firefox中的用户执行任何操作时,我希望能够显示一个弹出窗口或某种通知,说明“您已注销,因为您已从其他地方登录”

我有一个sessionListener,他跟踪创建和销毁的会话。问题是,我可以将HTTPSession对象保存在应用程序范围的bean中,并在检测到用户已登录两次时销毁它。但有些事情告诉我,这是错误的,是行不通的

JSF是否在服务器端某处跟踪会话?如何通过标识符访问它们?如果没有,当用户登录两次时,如何取消其第一次登录

  • 在数据库中创建一个整数字段
    userLoggedInCount
  • 每次登录时,递增该标记并将结果存储在会话中
  • 在每个请求中,检查数据库中的值和会话中的值,如果会话中的值小于数据库中的值,则,
    invalidate()
    会话并减小数据库中的值
  • 每当会话被破坏时,也会减小该值

  • 独立于数据库的方法是让
    用户
    拥有一个
    静态映射
    变量并实现(和)。这样,您的webapp在发生意外崩溃后仍能正常工作,这可能会导致DB值无法更新(当然,您可以创建一个
    ServletContextListener
    ,在webapp启动时重置DB,但这只是越来越多的工作)

    以下是
    用户
    的外观:

    public class User implements HttpSessionBindingListener {
    
        // All logins.
        private static Map<User, HttpSession> logins = new ConcurrentHashMap<>();
    
        // Normal properties.
        private Long id;
        private String username;
        // Etc.. Of course with public getters+setters.
    
        @Override
        public boolean equals(Object other) {
            return (other instanceof User) && (id != null) ? id.equals(((User) other).id) : (other == this);
        }
    
        @Override
        public int hashCode() {
            return (id != null) ? (this.getClass().hashCode() + id.hashCode()) : super.hashCode();
        }
    
        @Override
        public void valueBound(HttpSessionBindingEvent event) {
            HttpSession session = logins.remove(this);
            if (session != null) {
                session.invalidate();
            }
            logins.put(this, event.getSession());
        }
    
        @Override
        public void valueUnbound(HttpSessionBindingEvent event) {
            logins.remove(this);
        }
    
    }
    
    然后它将调用
    valueBound()
    ,这将从
    logins
    映射中删除任何以前登录的用户,并使会话无效

    当您按如下方式注销
    用户时:

    User user = userDAO.find(username, password);
    if (user != null) {
        sessionMap.put("user", user);
    } else {
        // Show error.
    }
    
    sessionMap.remove("user");
    

    或者,当会话超时时,将调用
    valueUnbound()
    ,从而将用户从
    登录图中删除。

    我喜欢使用HttpSessionBindingListener的BalusC给出的答案

    但在Enterprise JavaBeansTM规范2.0版中,有这样一条规定:

    企业Bean不能使用读/写静态字段。使用只读静态字段是非常困难的 允许。因此,建议企业bean类中的所有静态字段 宣布为最终决定

    所以,制作一个应用程序范围的Bean来存储整个应用程序范围内的表而不使用静态字段不是更好吗

    它试过了,似乎很管用

    以下是我的例子:

    @Named
    @ApplicationScoped
    public class UserSessionStorage implements java.io.Serializable,HttpSessionBindingListener {
    
    @Inject
    UserManagement userManagement;
    
    private static final long serialVersionUID = 1L;
    
    /**
     * Application wide storage of the logins
     */
    private final Map<User, List<HttpSession>> logins = new HashMap<User, List<HttpSession>>();
    
    @Override
    public void valueBound(final HttpSessionBindingEvent event) {
        System.out.println("valueBound");
    
        /**
         * Get current user from userManagement...
         */
        User currentUser = userManagement.getCurrentUser();
    
        List<HttpSession> sessions = logins.get(currentUser);
        if (sessions != null) {
            for (HttpSession httpSession : sessions) {
                httpSession.setAttribute("invalid", "viewExpired");
            }
        } else {
            sessions = new ArrayList<HttpSession>();
        }
        HttpSession currentSession = event.getSession();
        sessions.add(currentSession);
        logins.put(currentUser, sessions);
    }
    
    @Override
    public void valueUnbound(final HttpSessionBindingEvent event) {
        System.out.println("valueUnbound");
    
        User currentUser = userManagement.getCurrentUser();
    
        List<HttpSession> sessions = logins.get(currentUser);
        if (sessions != null) {
            sessions.remove(event.getSession());
        } else {
            sessions = new ArrayList<HttpSession>();
        }
        logins.put(currentUser, sessions);
    }
    
    @Named
    @适用范围
    公共类UserSessionStorage实现java.io.Serializable、HttpSessionBindingListener{
    @注入
    用户管理用户管理;
    私有静态最终长serialVersionUID=1L;
    /**
    *登录的应用程序范围存储
    */
    private final Map logins=new HashMap();
    @凌驾
    public void valueBound(最终HttpSessionBindingEvent事件){
    System.out.println(“valueBound”);
    /**
    *从userManagement获取当前用户。。。
    */
    User currentUser=userManagement.getCurrentUser();
    列表会话=logins.get(currentUser);
    如果(会话!=null){
    用于(HttpSession HttpSession:会话){
    httpSession.setAttribute(“无效”、“视图过期”);
    }
    }否则{
    sessions=newarraylist();
    }
    HttpSession currentSession=event.getSession();
    sessions.add(当前会话);
    logins.put(当前用户、会话);
    }
    @凌驾
    public void valueUnbound(最终HttpSessionBindingEvent事件){
    System.out.println(“valueUnbound”);
    User currentUser=userManagement.getCurrentUser();
    列表会话=logins.get(currentUser);
    如果(会话!=null){
    移除(event.getSession());
    }否则{
    sessions=newarraylist();
    }
    logins.put(当前用户、会话);
    }
    
    }


    ->对不起,我的英语…

    我想我可以使用应用程序范围的类来代替DB。例如,问题是如何从阶段侦听器访问它?好的解决方案。你可以通过FacesContext获取外部上下文。这个算法可能不够。这一理论是完美的,但在实践中,如果浏览器将会话保存在cookie中并从中还原回来,它将不起作用。请看步骤2。如我所说,如果浏览自动恢复会话,它将不会通过任何LoginHandler或LoginMethod或任何可控位置来执行该标志的增量。在这种情况下如何行动?谢谢你的回答。我猜“sessionMap.put(“user”,user);”应该是“sessionMap.put(username,user);”。否则,如果具有不同凭据的不同用户登录,我们会将第一个用户踢出。这是不正常的。您不希望在一个客户端会话期间有不同的登录用户。另外,不要将会话映射与应用程序映射混淆。好的,我现在知道sessionMap是ExternalContext.sessionMap。它可以工作:)。当调用
    valueUnbound()
    时,您不想
    使会话失效吗?@Harry:不,甚至,实际上是无效本身导致了方法被调用:)HttpSessionBindingListener不是EJB。只有那些带有
    javax.ejb.*
    主注释的类,例如
    @Stateless
    才是ejb。