我用的是servletcontext简单易操作,而且整个服务器端可以共享数据,服务器启动自动生成,关闭即销毁。刚好符合需求。
项目结构
直接开始代码;
config里是不同类获取session的方法
获取request对象 GetRequest类
public class GetRequest {
public static HttpServletRequest getRequest() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
return request;
}
}
已经得到request对象了,那得到其他的bean就很简单了。
获取session GetSession类
public class GetSession {
public static HttpSession getSession(){
HttpServletRequest request = GetRequest.getRequest();
HttpSession session = request.getSession();
return session;
}
}
获得servletcontext了,这个要是方法不对会报空指针异常,这个问题要重视 GetServletContext类
public class GetServletContext {
public static ServletContext getServletContext() {
ServletContext servletContext = GetSession.getSession().getServletContext();
return servletContext;
}
}
config里还有两个类分别是检查session的方法CheckSession,和移除session的方法DelSecondUser
CheckSession类
检查session的方法,判断当前session中是否有用户登录信息,没有的话说明没登录,返回true,如果存在登录信息,取出当前sessionId以及servletcontext中此用户id对应的sessionId,判断两个sessionId是否相等,相等的话,说明是同一个连接会话,也返回true。不相等的话,就说明后面登录的sessionId已经覆盖掉了之前servletcontext中的sessionId,存在异地登录,返回false。
public class CheckSession {
public static boolean checkSession() {
HttpSession session = GetSession.getSession();
User userInfo = (User) session.getAttribute("loginUser");
if (userInfo == null) {
return true;
} else {
ServletContext servletContext = GetServletContext.getServletContext();
String sessionId = session.getId();
String userId = String.valueOf(userInfo.getId());
String oldSessionId = (String) servletContext.getAttribute(userId);
System.out.println("old---------------"+oldSessionId);
System.out.println("old---------------"+sessionId);
/*如果不存在此用户的sessionId(一般不可能) 或者新旧id相等,说明是同一个登录*/
if (oldSessionId == null || oldSessionId.equals(sessionId)) {
return true;
} else {
/*否则就是不同客户端登录,返回false*/
return false;
}
}
}
}
DelSecondUser类
移除session的方法,我这里每个页面拦截判断后都需要执行。
public class DelSecondUser {
public static void delSecondUser(){
HttpSession session = GetRequest.getRequest().getSession();
session.removeAttribute("loginUser");
/*存储一个状态,用于前端判断是多客户端登录,账号被挤掉了*/
session.setAttribute("loginState","1");
}
}
再看登录后
登录成功时,将用户信息存入session中,取出sessionId和用户Id,以用户id为key(因为每个用户id都是唯一的,sessionId会改变的)。
User userInfo = userService.findByAccountAndPassword(name, password);
/*登录成功把用户信息存入session中,便于调用*/
session.setAttribute("loginUser", userInfo);
/*获取当前用户id并初始化为String类型*/
String userId = String.valueOf(userInfo.getId());
/*获取当前会话的sessionId*/
String sessionId = session.getId();
ServletContext servletContext = GetServletContext.getServletContext();
/*以用户id为key,sessionId为value存储到容器中*/
servletContext.setAttribute(userId, sessionId);
session.removeAttribute("loginState");
return "successful";
之后去拦截器那里进行判断:
这里我用的是登录成功后的应该界面的跳转,来测试功能有没有实现,如果实现的话,我们在例外一个浏览器登录是就会把最开始登录的那个浏览器挤掉,进行操作的话就需要从新登录
@RequestMapping({"/index", ""})
public String index(ModelMap modelMap) {
if (!CheckSession.checkSession()) {
DelSecondUser.delSecondUser();
return "login";
} else {
return "popup";
}
}
至于怎么在登录页判断是不是被挤掉账号而过来的进行提示
记得我们在移除session的那个方法里写了这样一个语句。
session.setAttribute("loginState","1");
/*存储一个状态,用于前端判断是多客户端登录,账号被挤掉了*/
如果是被挤到的,那么给前端一个1的状态。
然后在登录页放一个隐藏的标签来接收它:
<span style="display:none;"th:if="${session.loginState!=null}"th:text="${session.loginState}" id="ifLogin"></span><!--如果是1代表是被挤掉而重定向过来的-->
<span style="display: none;" th:if="${session.loginState==null}" th:text="0" id="ifLogin"></span><!--默认是0-->
在登录页的js里写一个判断方法:
//判断是不是被挤出登录
function ifLogin(){
var state = $('#ifLogin').text().trim();
if (state == "1"){
layer.confirm('您的账号在其他地方登录,请确认是否本人操作?', {
btn: ['是我本人操作','不是我,立即修改密码'] //按钮
,closeBtn:0
,title:"警告"
,btnAlign: 'c'
}, function(){
layer.msg('好的!请注意保护个人账号安全!',{icon:6});
}, function(){
location.href="forgetPd";
});
}
}
每次进入页面都进行判断,是不是1,是1就给出提示。
如果在登录界面再次登录,那么就移除掉这loginstate状态,前面登录那里有写到。
最后就完成了,这个登录
问题可能会出现在ServletContext的获取上,可以去查查怎么获取这个值