目前web项目中,很多情况都是可以让同一个账户信息在不同的登录入口登录这次,这样子就不那么美好了。
现在有两种解决方案:
1、将用户的登录信息用一个标志位的字段保存起来,每次登录成功就标记1,注销登录就标记为0,当标记为1的时候不允许别人登录。
2、将用户的登录信息保存在application内置作用域内, 然后利用session监听器监听每一个登录用户的登录情况。
很显然,第一种方式 每次登录 都需要操作数据库,多了一些不必要的性能开销,而且在登录状态下 万一突然电脑关闭了,那就永远都不能登录了,可用性比较低。
但是第二种方式就不一样了,可操作性强,很方便维护所有在线用户的信息。
接下来 主要介绍第二种方式的具体实现:
1、在处理登录的login方法中,先查询数据库验证下该用户是否存在,如果存在 判断该登录账户是否已经锁定了, 然后从application内置作用域对象中取出所有的登录信息,查看该username账户是否已经登录,如果登录了,就友好提示下,反之表示可以登录,将该登录信息保存在application中。
代码如下:
[java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
//没有使用零配置前 每个访问的方法都要加上@Action ,否则404
@Action
(value=
"login"
, results={
@Result
(name=
"index"
, location=
"index.jsp"
),
})
public
String login()
throws
Exception {
try
{
User result = userService.login(user.getFuUserName(), user.getFuPassword());
if
(result!=
null
){
if
(result.getFuStatus()!=
null
&& result.getFuStatus()==
0
){
super
.setRequestAttr(Constant.MESSAGE,
"抱歉,该用户已被锁定!"
);
return
"error"
;
}
Map<String, String> loginUserMap = (Map<String, String>)
super
.getApplicationAttr(Constant.LOGIN_USER_MAP);
boolean
isExist =
false
;
String sessionId =
super
.getSessionId(
false
);
if
(loginUserMap==
null
){
loginUserMap =
new
HashMap<String, String>();
}
for
(String username : loginUserMap.keySet()) {
//判断是否已经保存该登录用户的信息 并且 如果是同一个用户进行重复登录那么允许登录
if
(!username.equals(result.getFuUserName()) || loginUserMap.containsValue(sessionId)){
continue
;
}
isExist =
true
;
break
;
}
if
(isExist){
super
.setRequestAttr(Constant.MESSAGE,
"抱歉,该用户已登录!"
);
return
"error"
;
}
else
{
loginUserMap.put(result.getFuUserName(), sessionId);
}
//登录成功
super
.setSessionAttr(Constant.LOGIN_USER, result);
super
.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);
logger.info(result.getFuUserName() +
" 登录成功!"
);
//如果 session中fromUrl有值,就跳转到该页面
String fromUrl = (String)
super
.getSessionAttr(Constant.FROM_URL);
if
(fromUrl!=
null
){
super
.setSessionAttr(Constant.FROM_URL,
null
);
super
.getResponse().sendRedirect(fromUrl.toString());
return
null
;
}
return
"index"
;
}
}
catch
(Exception e) {
e.printStackTrace();
logger.info(
"登录失败: "
+e.getMessage());
}
super
.setRequestAttr(
"message"
,
"用户名或密码错误"
);
return
"error"
;
}
//没有使用零配置前 每个访问的方法都要加上@Action ,否则404
@Action
(value=
"login"
, results={
@Result
(name=
"index"
, location=
"index.jsp"
),
})
public
String login()
throws
Exception {
try
{
User result = userService.login(user.getFuUserName(), user.getFuPassword());
if
(result!=
null
){
if
(result.getFuStatus()!=
null
&& result.getFuStatus()==
0
){
super
.setRequestAttr(Constant.MESSAGE,
"抱歉,该用户已被锁定!"
);
return
"error"
;
}
Map<String, String> loginUserMap = (Map<String, String>)
super
.getApplicationAttr(Constant.LOGIN_USER_MAP);
boolean
isExist =
false
;
String sessionId =
super
.getSessionId(
false
);
if
(loginUserMap==
null
){
loginUserMap =
new
HashMap<String, String>();
}
for
(String username : loginUserMap.keySet()) {
//判断是否已经保存该登录用户的信息 并且 如果是同一个用户进行重复登录那么允许登录
if
(!username.equals(result.getFuUserName()) || loginUserMap.containsValue(sessionId)){
continue
;
}
isExist =
true
;
break
;
}
if
(isExist){
super
.setRequestAttr(Constant.MESSAGE,
"抱歉,该用户已登录!"
);
return
"error"
;
}
else
{
loginUserMap.put(result.getFuUserName(), sessionId);
}
//登录成功
super
.setSessionAttr(Constant.LOGIN_USER, result);
super
.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);
logger.info(result.getFuUserName() +
" 登录成功!"
);
//如果 session中fromUrl有值,就跳转到该页面
String fromUrl = (String)
super
.getSessionAttr(Constant.FROM_URL);
if
(fromUrl!=
null
){
super
.setSessionAttr(Constant.FROM_URL,
null
);
super
.getResponse().sendRedirect(fromUrl.toString());
return
null
;
}
return
"index"
;
}
}
catch
(Exception e) {
e.printStackTrace();
logger.info(
"登录失败: "
+e.getMessage());
}
super
.setRequestAttr(
"message"
,
"用户名或密码错误"
);
return
"error"
;
}
|
2、登录入口处理完之后,考虑到回话结束的话,那么对应的登录用户也应该相应的注销登录。我们可以写一个Session监听器,监听sessioon销毁的时候,我们将登录的用户注销掉,也就是从application中移除。表示该用户已经下线了。
代码如下:
[java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
public
class
SessionListener
implements
HttpSessionListener,ServletContextListener{
private
int
count;
private
ServletContext servletContext =
null
;
public
SessionListener() {
count =
0
;
}
private
Logger logger = Logger.getLogger(
this
.getClass());
@Override
public
void
sessionCreated(HttpSessionEvent event) {
count++;
setContext(event);
logger.info(
"***************the http session is created...***************"
);
}
@Override
public
void
sessionDestroyed(HttpSessionEvent event) {
//在session销毁的时候 把loginUserMap中保存的键值对清除
User user = (User)event.getSession().getAttribute(
"loginUser"
);
if
(user!=
null
){
Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute(
"loginUserMap"
);
loginUserMap.remove(user.getFuUserName());
event.getSession().getServletContext().setAttribute(
"loginUserMap"
,loginUserMap);
}
count--;
setContext(event);
logger.info(
"***************the http session is destroyed...***************"
);
}
public
void
setContext(HttpSessionEvent httpSessionEvent){
httpSessionEvent.getSession().getServletContext().setAttribute(
"online"
, count);
}
@Override
public
void
contextDestroyed(ServletContextEvent servletcontextevent) {
this
.servletContext =
null
;
logger.info(
"***************the servlet context is destroyed...***************"
);
}
@Override
public
void
contextInitialized(ServletContextEvent servletcontextevent) {
this
.servletContext = servletcontextevent.getServletContext();
logger.info(
"***************the servlet context is initialized...***************"
);
}
public
class
SessionListener
implements
HttpSessionListener,ServletContextListener{
private
int
count;
private
ServletContext servletContext =
null
;
public
SessionListener() {
count =
0
;
}
private
Logger logger = Logger.getLogger(
this
.getClass());
@Override
public
void
sessionCreated(HttpSessionEvent event) {
count++;
setContext(event);
logger.info(
"***************the http session is created...***************"
);
}
@Override
public
void
sessionDestroyed(HttpSessionEvent event) {
//在session销毁的时候 把loginUserMap中保存的键值对清除
User user = (User)event.getSession().getAttribute(
"loginUser"
);
if
(user!=
null
){
Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute(
"loginUserMap"
);
loginUserMap.remove(user.getFuUserName());
event.getSession().getServletContext().setAttribute(
"loginUserMap"
,loginUserMap);
}
count--;
setContext(event);
logger.info(
"***************the http session is destroyed...***************"
);
}
public
void
setContext(HttpSessionEvent httpSessionEvent){
httpSessionEvent.getSession().getServletContext().setAttribute(
"online"
, count);
}
@Override
public
void
contextDestroyed(ServletContextEvent servletcontextevent) {
this
.servletContext =
null
;
logger.info(
"***************the servlet context is destroyed...***************"
);
}
@Override
public
void
contextInitialized(ServletContextEvent servletcontextevent) {
this
.servletContext = servletcontextevent.getServletContext();
logger.info(
"***************the servlet context is initialized...***************"
);
}
web.xml中配置如下:
[html] view plaincopyprint?
<!-- session listener -->
<listener>
<listener-
class
>com.facelook.util.SessionListener</listener-
class
>
</listener>
<!-- session listener -->
<listener>
<listener-
class
>com.facelook.util.SessionListener</listener-
class
>
</listener>
|
3、另外,还有一个问题,如果说登录的用户突然关闭了浏览器而没有点击退出按钮。那么可以利用beforeunload 事件,在浏览器刷新或者关闭的时候触发。
[java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//在刷新或关闭时调用的事件
$(window).
bind
(
'beforeunload'
,
function
(){
$.ajax({
url:
"${ctx}/system/user/user!logout.action"
,
type:
"post"
,
success:
function
(){
alert(
"您已退出登录"
);
}
});
);
//在刷新或关闭时调用的事件
$(window).
bind
(
'beforeunload'
,
function
(){
$.ajax({
url:
"${ctx}/system/user/user!logout.action"
,
type:
"post"
,
success:
function
(){
alert(
"您已退出登录"
);
}
});
});
|
这样子,基本上可以实现防止多用户登录的案例了!