Java 防止使用相同的用户名和密码多次登录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1932091/
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
Prevent multiple login using the same user name and password
提问by vivmal
I am developing an application that needs to prevent multiple login using the same user name and password.
我正在开发一个应用程序,需要防止使用相同的用户名和密码进行多次登录。
If it happens on the same machine then obviously we need to do something with the user session, but it should also prevent if they are login on different machines using the same user name and password.
如果它发生在同一台机器上,那么显然我们需要对用户会话做一些事情,但如果他们使用相同的用户名和密码在不同的机器上登录,它也应该防止。
We have to keep following things in mind:
我们必须牢记以下几点:
- If user close the browser without logout.
- If session time out.
- 如果用户在没有注销的情况下关闭浏览器。
- 如果会话超时。
I would appreciate any help on this.
我将不胜感激。
回答by Shantanu Gupta
Take one extra field in table with the column name say "IsLoggedIn" as bit field and set it to true until the user is logged in. As soon as user logs out set it to false. This need to be done for session expiry time also. As soon as the session expires this field should be set to false automatically using triggers or thru SP call
将表中的一个额外字段与列名说“IsLoggedIn”作为位字段并将其设置为真,直到用户登录。一旦用户注销,将其设置为假。这也需要在会话到期时间完成。一旦会话过期,该字段应使用触发器或通过 SP 调用自动设置为 false
good solution is still welcome
好的解决方案仍然受欢迎
回答by ojrac
I'd track each user's last known IP address and a timestamp for when they were last on that IP. Then you can just block access from other IPs for 5 minutes, an hour, or whatever you like.
我会跟踪每个用户的最后一个已知 IP 地址和他们最后一次使用该 IP 的时间戳。然后,您可以阻止来自其他 IP 的访问 5 分钟、一个小时或您喜欢的任何时间。
Whenever the IP address switches, you can a) expire the user's old session, so they're forced to log back in and b) increment a per-user counter (which you can zero out every hour). If the counter goes above 5 (or something), you can block all access to the user's account for a longer period of time.
每当 IP 地址切换时,您可以 a) 使用户的旧会话过期,因此他们被迫重新登录,并且 b) 增加每个用户的计数器(您可以每小时将其清零)。如果计数器超过 5(或其他值),您可以在更长的时间内阻止对用户帐户的所有访问。
回答by Peter Lang
You could store some sort of session-id for the user when logging in. When the user logs out or when the session expires, you remove that information again.
您可以在登录时为用户存储某种会话 ID。当用户注销或会话过期时,您再次删除该信息。
When a user tries to log in, and you already have a session-id stored for this user, let the user confirm that, and then invalidate the old session.
当用户尝试登录时,您已经为该用户存储了会话 ID,让用户确认,然后使旧会话无效。
A user will certainly want to login again immediately if the browser crashed or something like that, so letting the user wait for the session to expire might be annoying.
如果浏览器崩溃或类似的情况,用户肯定希望立即再次登录,因此让用户等待会话过期可能会很烦人。
Does this make sense for your application?
这对您的应用程序有意义吗?
回答by Bozho
I would also advise for Shantanu Gupta's solution - have a database column indicating the the user is currently logged, and update that column accordingly.
我还建议 Shantanu Gupta 的解决方案 - 有一个数据库列指示用户当前已登录,并相应地更新该列。
In order to 'capture' session expiration, you need to define in your web.xml
:
为了“捕获”会话过期,您需要在您的web.xml
:
<listener>
<listener-class>com.foo.MySessionListener</listener-class>
</listener>
Where MySessionListener
is your implementation of the HttpSessionListener
interface (provided by the Servlet API).
接口MySessionListener
的实现在哪里HttpSessionListener
(由 Servlet API 提供)。
回答by BalusC
If user close the browser without logout.
如果用户在没有注销的情况下关闭浏览器。
Particularly this case is hard and not reliable to detect. You could use the beforeunload
event in Javascript, but you're fully dependent on whether the browser has JS enabled and the particular browser supports this non-standard event (e.g. Opera doesn't). That's also one of the major reasons that I'd suggest to just logout the previously logged in user instead of preventing the login. That's also more user-friendly and secure for the case that the user "forgot" to logout from the other computer.
特别是这种情况很难检测,也不可靠。您可以beforeunload
在 Javascript 中使用该事件,但您完全取决于浏览器是否启用了 JS 以及特定浏览器是否支持此非标准事件(例如 Opera 不支持)。这也是我建议只注销先前登录的用户而不是阻止登录的主要原因之一。对于用户“忘记”从另一台计算机注销的情况,这也更加用户友好和安全。
Easiest way is to let the User
have a static Map<User, HttpSession>
variable and let it implement HttpSessionBindingListener
(and Object#equals()
and Object#hashCode()
).
最简单的方法是让User
有一个static Map<User, HttpSession>
变量并让它实现HttpSessionBindingListener
(和Object#equals()
和Object#hashCode()
)。
public class User implements HttpSessionBindingListener {
// All logins.
private static Map<User, HttpSession> logins = new HashMap<User, HttpSession>();
// 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) {
request.getSession.setAttribute("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
,如下所示:
request.getSession().removeAttribute("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 Andrzej Doyle
I would simply suggest using a security framework to handle all these details for you. Spring Security, for example, is fairly easy to integrate into an existing project, can be customised quite heavily if needs be - and most importantly, it has built-in support for detecting and controlling concurrent logins.
我只是建议使用安全框架来为您处理所有这些细节。 例如,Spring Security很容易集成到现有项目中,可以根据需要进行大量定制 - 最重要的是,它具有检测和控制并发登录的内置支持。
Don't reinvent the wheel when it's not needed, else you'll end up spending a good bit of time to create a bumpy wheel.
不要在不需要的时候重新发明轮子,否则你最终会花费大量时间来制造一个颠簸的轮子。
回答by ZZ Coder
This can be easily enforced if you have session. For each browser login, you should create a session record in session DB. The session ID can be used as authentication cookie. The session DB also has an index with username. At login, you can query the database to check how many sessions are there. We actually allows one session for each type. For example, user can have a login from mobile phone and another one from browser. But it can't have 2 browser sessions.
如果您有会话,这可以很容易地执行。对于每次浏览器登录,您应该在会话数据库中创建一个会话记录。会话 ID 可用作身份验证 cookie。会话数据库还有一个带有用户名的索引。在登录时,您可以查询数据库以检查有多少会话。我们实际上允许每种类型有一个会话。例如,用户可以从手机登录,从浏览器登录。但它不能有 2 个浏览器会话。
To solve the problem you mentioned. You have 2 options,
为了解决你提到的问题。你有2个选择,
Have a very short session timeout (like 5 minutes) and extend session on every use. This way, user will be automatically logged out if leaving without logging out.
Bump the other session. The new session bumps the old session. The bumped session stays in DB with a special flag for 24 hours. We display a message to tell user the other session is being bumped and displays time and IP. This way, user will get notified if their account is being compromised.
有一个非常短的会话超时(如 5 分钟)并在每次使用时延长会话。这样,如果用户没有退出,用户将自动退出。
颠簸另一个会话。新会话与旧会话相撞。碰撞的会话在 DB 中保留一个特殊标志 24 小时。我们显示一条消息告诉用户另一个会话正在被碰撞并显示时间和 IP。这样,如果他们的帐户被盗用,用户将收到通知。
回答by mastermind4242
Create one table in your database — let's call it [online_users]
— with three fields:
在您的数据库中创建一个表 - 我们称之为[online_users]
- 包含三个字段:
[online_users]
1. username
2. login_time
3. logout_time
Whenever a user logs in, insert the user's name and the login time into [online_users]
.
每当用户登录时,将用户名和登录时间插入到[online_users]
.
On all pages which require users to log in, place this condition: check [online_users]
to see if the user's logout_time
is blank or not.
在所有需要用户登录的页面上,放置这个条件:检查[online_users]
用户是否logout_time
为空白。
Whenever a user presses a logout button, set the logout_time
in [online_users]
for that user's name.
每当用户按下注销按钮时,请为该用户的名称设置logout_time
in [online_users]
。
If someone tries to log in with an active username and password, check for username
and logout_time
and display a message stating that the user is already logged in. And, most importantly, set logout_time
to MULTIPLELOGIN
for that user.
如果有人试图登录与有效的用户名和密码,检查username
和logout_time
,并显示一条消息,说明用户已经登录。而且,最重要的是,一套logout_time
给MULTIPLELOGIN
该用户。
If that user is logged in on any other machine, then if he refreshes or navigates to another page the site will tell him that he has been logged out. Then, the user can be redirected to the homepage of the site.
如果该用户在任何其他机器上登录,那么如果他刷新或导航到另一个页面,站点将告诉他他已注销。然后,用户可以被重定向到该站点的主页。
回答by Theresa Forster
I have implemented a possible solution for myself,
我已经为自己实施了一个可能的解决方案,
in the loginFilter I use, I set a lastloggedin, userloggedin and userSession within the user record on my system.
在我使用的 loginFilter 中,我在系统上的用户记录中设置了 lastloggedin、userloggedin 和 userSession。
user.setUser_lastlogged(new Date());
user.setUser_loggedin(true);
user.setSessionId(request.getSession().getId());
appService.saveUsers(user);
so when i go to any of my struts2 actions i have a snippet of code in the prepare method.
因此,当我执行任何 struts2 操作时,我在准备方法中有一段代码。
@Override
public void prepare() throws Exception {
UsersBase usercheck = appservice.getUserByUsername((String)request.getSession().getAttribute("j_username"));
if(request.getSession().getId().equals(usercheck.getSessionId())){
request.getSession().invalidate();
}
}
This will log the user out when they login on another machine, or if you don't want to log them in i could do the following on the loginFilter
这将使用户在另一台机器上登录时注销,或者如果您不想让他们登录,我可以在 loginFilter 上执行以下操作
UsersBase userdto = appService.getUserByUsername(username);
if (userdto != null) {
if ((userdto.getUser_loggedin())) {
if (request.getSession().getId().equals(userdto.getSessionId())) {
authRequest.eraseCredentials();
request.getSession().setAttribute("error", "You are already logged in ");
}
}
}
回答by Toren
Maybe overly simplified, but hey... it works for me in Web2Py:
也许过于简化了,但是嘿...它在 Web2Py 中对我有用:
Only on successful login, I am writing the SessionID (response.session_id) in the auth_membership table. On the landing page (index page) I check whether the current response.session_id is equal to the SessionID coming from the DB. If so - all is fine. If not - (the "older" , first) user is politely logged out.
只有在成功登录后,我才会在 auth_membership 表中写入 SessionID (response.session_id)。在登录页面(索引页面)上,我检查当前的 response.session_id 是否等于来自数据库的 SessionID。如果是这样 - 一切都很好。如果不是 - (“老”,首先)用户被礼貌地注销。
The above works since with each login a NEW response.session_id is created and stored in the DB. The checking is done only on the landing page (which in my app is the most important one, initiating many other functions), so not too many DB hits for the above. The above is not dependent on the user logging out. No IP address is involved (which others have mentioned, suffers from its own issues) It allows only ONE user to be logged in at a time and it logs out the "older" user.
上述工作是因为每次登录都会创建一个 NEW response.session_id 并将其存储在数据库中。检查仅在登录页面上完成(在我的应用程序中是最重要的页面,启动了许多其他功能),因此上述的 DB 命中次数不会太多。以上不依赖于用户注销。不涉及 IP 地址(其他人已经提到过,有自己的问题)它一次只允许一个用户登录,并注销“旧”用户。
Hope it helps NeoToren
希望它能帮助 NeoToren