如何使用相同的凭据记录两次用户会话时使其无效

时间:2010-03-03 15:04:49

标签: java session jsf richfaces facelets

我正在使用带有Richfaces和Facelets的JSF 1.2。

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

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

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

另外。当Firefox中的用户做任何事情时,我希望能够显示一个弹出窗口或某种通知,说“你已经登出,因为你已经从其他地方登录了”。

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

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

3 个答案:

答案 0 :(得分:20)

独立于数据库的方法是让User拥有static Map<User, HttpSession>变量并实施HttpSessionBindingListener(以及Object#equals()Object#hashCode())。这样你的webapp仍会在意外崩溃后仍然可以运行,这可能导致数据库值不会更新(当然你可以创建一个ServletContextListener来重置webapp启动时的数据库,但这只是越来越多的工作)。

以下是User的外观:

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);
    }

}

当您按如下方式登录User时:

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

然后它将调用valueBound(),它将从logins地图中删除任何先前登录的用户并使会话无效。

当您按如下方式退出User时:

sessionMap.remove("user");

或当会话超时时,将调用valueUnbound(),从logins地图中移除用户。

答案 1 :(得分:4)

  1. 在数据库userLoggedInCount
  2. 中创建一个整数字段
  3. 在每个登录增量上标记并将结果存储在会话中。
  4. 在每个请求中检查数据库中的值和会话中的值,如果会话中的值小于数据库中的值,则invalidate()会话并递减数据库中的值< / LI>
  5. 每当会话被销毁时,也会减少该值。

答案 2 :(得分:1)

我喜欢BalusC使用HttpSessionBindingListener的答案。

但是在Enterprise JavaBeansTM Specification,Version 2.0中写道:

  

企业Bean不得使用读/写静态字段。使用只读静态字段是   允许。因此,建议企业bean类中的所有静态字段都是   宣布为最终

因此,如果不使用静态字段来创建一个存储表应用程序范围的ApplicationScoped 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);
}

}

- &GT;对不起我的änglish...