最近实现项目中的用户重复登录控制,即新用户登录使前一个登录的用户强制下线 ,思路是这样的,在前台主页面写一个程序定时向后台发送请求获取后台信息来判断session是否失效,若失效强制下线。
(SSM框架为例)
一 Controller层
1. 定义两个全局MAP 来存放用户的登录帐号(userId),session和sessionId
//userid,session
public static Map<String,HttpSession> MAP1 = new HashMap<String,HttpSession>();
//sessionId,userId
public static Map<String,String> MAP2= new HashMap<String,Sting>();
2 定义一个静态方法 存储用户session,检查是否登录 (记得在用户成功登录后调用此静态方法)
public static void userLogin(HttpServletRequset request){
String userId = request.getParameter("userId");
//当前用户
HttpSession session1 = request.getSession();
//当前用户session
String sessionId = request.getSession().getId();
//当前用户sessionId
MAP2.put(sessionId,userId);
//放入当前用户的sessionId 和 userId
HttpSession session = MAP1.remove(userId);//MAP1移除上个用户的session 若session为空 MAP1放入
if(session != null){
MAP2.remove(session.getId()); //session 不为空 根据sessionId 找到上个userId对应的session 移除
session.setAttribute("msg","您的帐号已在另一处登录"); //session移除后向前台发送信息
}
MAP1.put(userId,session1);
}
3.写一个方法向前台传值
@RequsetMapping(value="/check.do")
@RequseBody
public String checkLogin(HttpServletRequest request){
HttpSession session = request.getSession();
if(session.getAttribute("msg") != null ){
return ResponseUtil.getMsg(session.getAttribute("msg") );
//返回前台的信息,这里调用了一个封装好的返回工具类,可以单独使用io流返回值
}
}
二 前台(EXTJS为例)
1 前台主页面刷新不断向后台请求获取msg ,原理跟ajax轮循无异 , 在主页面中调用 checkLogin 方法
checkUser : function(){
Ext.Ajax.request({
method:"POST",
url:'Login/check.do' //向后台发送请求
success : function(response){
var responsJson = Ext.decode(response.responseText);
var success = responsJson.success;
if(success) {
Ext.Mag.confirm({
title : '提示',
msg : responsJson.msg, //从后台获取到的msg
buttons : Ext.Msg.YES,
fn : function(btn, text){
if(btn == 'yes'){
---前台的注销方法-- //调用一个注销方法
};
}
})
}
}
)}
}
checkLogin : function(){
check = setInterval(checkUser , 3000); //定时调用 checkUser方法
}
PS:这种方法有个弊端,因为项目中的注销方法是直接销毁session,所以在同一浏览器同一用户登录注销后再次登录时会报错,session已经销毁,MAP中还存在session,报错。
解决方法:在销毁session时判断一下,在销毁方法中加以下代码
if(session.getAttribute("msg")!null){
session.invalidate();
//正常销毁
}else{
MAP.remove(MAP2.remove(session.getId()));
//销毁之前先根据sessionId找到MAP2中的userId,再移除MAP1中的session;
session.invalidate();
}
解决!
————————————— 分割线——————————————————-
新增销毁监听,否则无法解决关闭浏览器session失效问题
public void sessionDestroyed ( HttpSessionEvent arg0){
HttpSession session = arg0.getSession();
synchronized (session) {
if(session.getAttribute("userSession")
instanceof LoginUserSessionBean){
LoginUserSession userSession =
( LoginUserSessionBean)session.getAttribute("userSession");
HttpSession beforeSession = LoginController.MAP1.remove(userSession.getLoginUserInfo().getUserId());
if(beforeSession != null){
LoginController.MAP2.remove(beforeSession.getId());
}
}
} }