20

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

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

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

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

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

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

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

4

3 回答 3

20

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

这是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地图中删除。

于 2010-03-03T16:25:57.833 回答
4
  1. 在数据库中创建一个整数字段userLoggedInCount
  2. 在每次登录时增加该标志并将结果存储在会话中。
  3. 在每个请求上检查数据库中的值和会话中的值,如果会话中的值小于数据库中的值,invalidate()则会话并减少数据库中的值
  4. 每当会话被销毁时,也会减少该值。
于 2010-03-03T15:22:40.890 回答
1

我喜欢来自 BalusC 的带有 HttpSessionBindingListener 的答案。

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

企业 Bean 不得使用读/写静态字段。允许使用只读静态字段。因此,建议将企业bean类中的所有静态字段声明为final

那么在不使用静态字段的情况下制作一个在应用程序范围内存储表的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);
}

}

-> 对不起我的英语...

于 2013-06-17T08:42:56.527 回答