相信我们在开发web工程的时候,都会遇到同一用户可以同时登录进系统的问题。对于开发的时候我们可能不是太关注这个问题,但对客户来说,他们肯定觉得是不可以的。那么我们在日常开发的过程中有几种解决方案。
1、如果我们的项目使用的是 Spring 框架的话,Spring security 就可以实现防止用户重复登录的问题。当然有些web工程可能对于权限的控制要求没有那么严格,单单为了一个登录功能去实现Spring security 就有些大材小用了。我们还有其他的解决方案。
2、数据库层面设计,可以为用户表加登录状态字段判断,登录成功后,修改状态为1,退出后修改状态为0。但是此种方案貌似也有缺陷,如果某个用户登录进系统之后,没有正常退出那就坑了,状态如何修改?肯定相应的要做个管理员清退功能,将该用户强制下线。此外每次登陆都去操作一下数据库是不是有些多余了呢?没关系,我们可以利用更简单的方式来实现。
3、我们可以利用 Servlet三大域对象 request、session、application(ServletContext)中的ServletContext,它是一个全局的储存信息的空间,服务器启动后就被创建,服务器停止后才销毁。
request,一个用户可有多个;session,一个用户一个;而servletContext,所有用户共用一个。所以,为了节省空间,提高效率,ServletContext中,要放必须的、重要的、所有用户需要共享的线程又是安全的一些信息。
具体方案:将用户的登录信息保存在application里, 然后利用session监听器HttpSessionListener监听每一个登录用户的登录情况。
1、在自己项目的登录功能中添加如下代码
ServletContext application = session.getServletContext();
@SuppressWarnings("unchecked")
Map<String, Object> loginMap = (Map<String, Object>) application.getAttribute("loginMap");
if (loginMap == null) {
loginMap = new HashMap<String, Object>();
}
for (String key : loginMap.keySet()) {
if (login.getUser_id().equals(key)) {
if (session.getId().equals(loginMap.get(key))) {
return redirectUrl.append("login.action?mesg=")
.append(URLEncoder.encode(username + "在同一地点重复登录", "UTF-8")).toString();
} else {
return redirectUrl.append("login.action?mesg=")
.append(URLEncoder.encode(username + "异地已登录,请先退出登录", "UTF-8")).toString();
}
}
}
loginMap.put(login.getUser_id(),session.getId());
application.setAttribute("loginMap", loginMap);
// 将用户保存在session当中
session.setAttribute("user", user);
// session 销毁时间
session.setMaxInactiveInterval(10*60);
2、编写一个实现类 SessionListener,重写HttpSessionListener
/**
* session监听器
*
* @author Fyq
*
*/
public class SessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
SysUserModel user = (SysUserModel) session.getAttribute("user");
if (user != null) {
ServletContext application = session.getServletContext();
@SuppressWarnings("unchecked")
Map<String, Object> loginMap = (Map<String, Object>) application.getAttribute("loginMap");
loginMap.remove(user.getUser_id());
application.setAttribute("loginMap", loginMap);
session.removeAttribute("user");
}
}
}
sessionCreated session创建的时候使用,这里如果我们需用做统计在线人数等功能时,可以自己去实现自己的逻辑代码了
sessionDestroyed session销毁的时候用,session 销毁时调用逻辑去清除登录信息。
上述session销毁时间可以按照自己实际需求去定。session监听器的作用是为了监听那些非正常退出系统的用户,清除登录信息用的。
3、退出系统的方法和监听器中销毁session方法中的逻辑大体相同,根据自身项目的实际情况去定
@RequestMapping(value = "/logout.action", method = RequestMethod.POST)
public void loginOut(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
String user_id = request.getParameter("user_id");
if (session != null) {
try {
session.removeAttribute("user");
String failSN = user_id + Contants.JOIN_STR;
if (session.getAttribute(failSN) != null) {
session.removeAttribute(failSN);
}
ServletContext application = session.getServletContext();
@SuppressWarnings("unchecked")
Map<String, Object> loginMap = (Map<String, Object>) application.getAttribute("loginMap");
loginMap.remove(user_id);
application.setAttribute("loginMap", loginMap);
} catch (Exception e) {
e.printStackTrace();
AJAXUtil.handleSuccess(response, false);
}
}
AJAXUtil.handleSuccess(response, true);
}
4、记得需要在web.xml中添加监听器
<listener>
<listener-class>com.csdn.framework.SessionListener</listener-class>
</listener>