Java 当他使用相同的凭据登录两次时如何使用户会话无效
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2372311/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to invalidate an user session when he logs twice with the same credentials
提问by pakore
I'm using JSF 1.2 with Richfaces and Facelets.
我将 JSF 1.2 与 Richfaces 和 Facelets 一起使用。
I have an application with many session-scoped beans and some application beans.
我有一个包含许多会话范围 bean 和一些应用程序 bean 的应用程序。
The user logs in with, let's say, Firefox. A session is created with ID="A"; Then he opens Chrome and logs in again with the same credentials. A session is created with ID="B".
用户使用 Firefox 登录。创建了一个 ID="A" 的会话;然后他打开 Chrome 并使用相同的凭据再次登录。创建一个 ID="B" 的会话。
When the session "B" is created, I want to be able to destroy session "A". How to do that?
创建会话“B”时,我希望能够销毁会话“A”。怎么做?
Also. when the user in Firefox does anything, I want to be able to display a popup or some kind of notification saying "You have been logged out because you have logged in from somewhere else".
还。当 Firefox 中的用户执行任何操作时,我希望能够显示一个弹出窗口或某种通知,说“您已被注销,因为您已从其他地方登录”。
I have a sessionListener who keeps track of the sessions created and destroyed. The thing is, I could save the HTTPSession object in a application-scoped bean and destroy it when I detect that the user has logged in twice. But something tells me that is just wrong and won't work.
我有一个 sessionListener 跟踪创建和销毁的会话。问题是,我可以将 HTTPSession 对象保存在应用程序范围的 bean 中,并在检测到用户已登录两次时销毁它。但是有些东西告诉我这是错误的并且行不通。
Does JSF keep track of the sessions somewhere on the server side? How to access them by identifier? If not, how to kick out the first log in of an user when he logs in twice?
JSF 是否在服务器端的某处跟踪会话?如何通过标识符访问它们?如果不是,如何踢掉用户两次登录时的第一次登录?
采纳答案by BalusC
The DB-independent approach would be to let the User
have a static Map<User, HttpSession>
variable and implement HttpSessionBindingListener
(and Object#equals()
and Object#hashCode()
). This way your webapp will still function after an unforeseen crash which may cause that the DB values don't get updated (you can of course create a ServletContextListener
which resets the DB on webapp startup, but that's only more and more work).
与数据库无关的方法是让User
有一个static Map<User, HttpSession>
变量并实现HttpSessionBindingListener
(和Object#equals()
和Object#hashCode()
)。这样,您的 web 应用程序在意外崩溃后仍将运行,这可能会导致 DB 值未更新(您当然可以创建一个ServletContextListener
在 web 应用程序启动时重置 DB 的方法,但这只是越来越多的工作)。
Here's how the User
should look like:
这是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);
}
}
When you login the User
as follows:
当您登录时User
,如下所示:
User user = userDAO.find(username, password);
if (user != null) {
sessionMap.put("user", user);
} else {
// Show error.
}
then it will invoke the valueBound()
which will remove any previously logged in user from the logins
map and invalidate the session.
然后它将调用 ,valueBound()
这将从地图中删除任何以前登录的用户logins
并使会话无效。
When you logout the User
as follows:
当您注销时User
,如下所示:
sessionMap.remove("user");
or when the session is timed out, then the valueUnbound()
will be invoked which removes the user from the logins
map.
或者当会话超时时,valueUnbound()
将调用将用户从logins
地图中删除。
回答by Bozho
- create an integer field in the databse
userLoggedInCount
- On each login increment that flag and store the result in the session.
- On each request check the value in the database and the one in the session, and if the one in the session is less than the one in the DB,
invalidate()
the session and decrement the value in the database - whenever a session is destroyed decrement the value as well.
- 在数据库中创建一个整数字段
userLoggedInCount
- 在每次登录时增加该标志并将结果存储在会话中。
- 在每个请求中检查数据库中的值和会话中的值,如果会话中的值小于
invalidate()
数据库中的值,则会话并递减数据库中的值 - 每当会话被销毁时,该值也会递减。
回答by Gatschet
I like the answer from BalusC with a HttpSessionBindingListener.
我喜欢带有 HttpSessionBindingListener 的 BalusC 的答案。
But in Enterprise JavaBeansTM Specification, Version 2.0 there is written:
但是在 Enterprise JavaBeansTM Specification, Version 2.0 中写到:
An enterprise Bean must not use read/write static fields. Using read-only static fields is allowed. Therefore, it is recommended that all static fields in the enterprise bean class be declared as final
企业 Bean 不得使用读/写静态字段。允许使用只读静态字段。因此,建议企业bean类中的所有静态字段都声明为final
So isnt't it better to make an ApplicationScoped Bean which store the table application wide without using static fields???
那么制作一个 ApplicationScoped Bean 来在不使用静态字段的情况下存储表应用程序不是更好吗???
It tried it out and it seems to work...
它尝试了它,它似乎有效......
Here is my example:
这是我的例子:
@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);
}
}
}
-> Sorry for my ?nglish...
-> 对不起我的英语...