在网上有许多单态登录的实例,但是大部分都是基于JSP,现在大型项目开发很少在Jsp中集成逻辑编码,因此本人基于纯后台总结一下单态登录的实现。
注:单态登陆和单点登录是两个不同的概念
(1)单态登录:目的是防止多台机器同时使用一个账号
(2)单点登录:用户只需要登录一次就可以访问所有相互信任的应用系统
1.单态登录实现步骤
(1)定义一个Javabean对象,已实现数据库连接
User.java
public class User implements java.io.Serializable{
private static final long serialVersionUID = 7873162303386188683L;
@Id
private Long id;
private String name;
private String password;
// 登录ip地址
@Transient
private static String ip;
//登录时间
@Transient
private static Date loginDate;
}
(2)DAO,Servlet实现数据库操作省略
(3)定义一个Controller,实现交互
UserController.java
@RequestMapping(value = "/check_login", method = RequestMethod.POST)
public ResponseResult checkLogin(@RequestBody User user,HttpServletRequest request, HttpSession session) {
User u = userService.login(user);
if (null != u) {
if (null != user) {
if (session != null) {
user.setId(u.getId());
user.setIp(request.getRemoteAddr());
user.setLoginDate(new java.util.Date());
user.setId(u.getId());
//将登录对象user放入session监听登录信息,触发监听器
request.getSession().setAttribute("user", user);
}
}
}
return ResponseResult.create(GlobalConstant.SUCCESS_CODE, null, new JSONObject());
}
2.单态登录监听器实现
(1)设置一个集合用来存放登录用户信息
public static final Map<String, HttpSession> loginUser = new HashMap<>();
(2)实现监听用户登录状态
public class LoginListener implements HttpSessionAttributeListener {
Log log= LogFactory.getLog(this.getClass());
@Override
/**
* HttpSessionBindingEvent:
* 1.当UserController中:request.getSession().setAttribute("user", user);
* 2.该事件在 监 听 到 HttpSession 发 生 绑 定 和 取 消 绑 定 的 情 况 时 连 通HttpSessionBindingListener
*/
public void attributeAdded(HttpSessionBindingEvent event){
String name = event.getName();
//如果用户已经登录,将已登录用户踢下线
if (name.equals("user")) {
User user = (User) event.getValue();
if (GlobalData.loginUser.get(user.getName()) != null) {
// map中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
HttpSession session = GlobalData.loginUser.get(user.getName());
User oldUser = (User) session.getAttribute("user");
log.info("账号"+oldUser.getName()+"在"+oldUser.getIp()+"已经登录,该登录将被迫下线!");
session.removeAttribute("user");
session.setAttribute("msg", "您的帐号已经在其他机器上登录,您被迫下线。");
}
//如果用户第一次登录,将session以用户名为索引,放入map中
GlobalData.loginUser.put(user.getName(), event.getSession());
log.info("账号" + user.getName()+"在" + user.getIp()+"登录");
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
String name = event.getName();
//注销
if (name.equals("user")) {
// 将该session从map中移除
User user = (User) event.getValue();
GlobalData.loginUser.remove(user.getName());
log.info("账号"+user.getName()+"注销");
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
String name = event.getName();
// 没有注销的情况下,用另一个帐号登录
if(name.equals("user")) {
// 移除旧的的登录信息
User oldUser = (User) event.getValue();
GlobalData.loginUser.remove(oldUser.getName());
// 新的登录信息
User user = (User) event.getSession().getAttribute("user");
// 也要检查新登录的帐号是否在别的机器上登录过
if(GlobalData.loginUser.get(user.getName()) != null) {
// map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
HttpSession session = GlobalData.loginUser.get(user.getName());
session.removeAttribute("user");
session.setAttribute("msg", "您的帐号已经在其他机器上登录,您被迫下线");
}
GlobalData.loginUser.put("user", event.getSession());
log.info("账号" + user.getName()+"在" + user.getIp()+"登录");
}
}
3.监听器web.xml配置
(1)在web.xml中配置编写好的LoginListener
(2)配置如下:******表示LoginListener 所在包
<listener>
<listener-class>******.LoginListener</listener-class>
</listener>
总结:至此一个完整的单态登录已经实现
(1)需要注意监听器LoginListener和控制层UserController的联通
(2)UserController中:request.getSession().setAttribute("user", user)发 生 绑 定 和 取 消 绑 定 的 情 况 时
(3)LoginListener:监听器将被触发
<1>HttpSessionBindingEvent event
<2> String name = event.getName();
(4)event.getName()获取到的便是request.getSession().setAttribute("user", user)中的对象