在门户网站一般都有这样的一个功能,你用另外一个设备或者在另一个网站登录,前一次登录的状态将会被挤下去。今天我们就使用监听器来实现这个功能。
主要的思路就是,在监听器内部建立一个map,key为当前登录用户信息计算出的token,value为对的会话session,如果需要对session的数据进行增删改则会触发监听器,判断用户是否已经登录,如果登录了,那么就在对印的session中删除用户登录信息。
工具类
package com.openlab.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
private static final ThreadLocal<SqlSession> SQLSESSION = new ThreadLocal<>();
private static SqlSessionFactory factory;
static {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
private MybatisUtil(){
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = SQLSESSION.get();
if (sqlSession == null){
SQLSESSION.set(factory.openSession());
return SQLSESSION.get();
}else {
return sqlSession;
}
}
public static void close(){
SqlSession sqlSession = SQLSESSION.get();
if (sqlSession!=null){
sqlSession.close();
SQLSESSION.set(null);
}
}
}
package com.openlab.utils;
import com.openlab.pojo.User;
public class TokenUtil {
private TokenUtil(){
}
//简单实现以下生成Token
public static String getToken(User user){
return user.hashCode()+"";
}
}
实体类
package com.openlab.pojo;
import java.util.Objects;
public class User {
private Integer id;
private String userName;
private String userPassword;
private String phone;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(userName, user.userName) &&
Objects.equals(userPassword, user.userPassword) &&
Objects.equals(phone, user.phone);
}
@Override
public int hashCode() {
return Objects.hash(id, userName, userPassword, phone);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", userPassword='" + userPassword + '\'' +
", phone='" + phone + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
映射文件和接口
package com.openlab.mapper;
import com.openlab.pojo.User;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
public User queryForLogin(@Param("userName") String userName,
@Param("password") String password);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.openlab.mapper.UserMapper">
<select id="queryForLogin" resultType="User">
SELECT
id,userName,userPassword,phone
FROM
smbms_user
WHERE
userName=#{userName} AND userPassword=#{password}
</select>
</mapper>
servlet
package com.openlab.servlet;
import com.openlab.mapper.UserMapper;
import com.openlab.pojo.User;
import com.openlab.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginServlet",value = "/login.do")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
String userName = request.getParameter("userName");
String password = request.getParameter("password");
User user = mapper.queryForLogin(userName, password);
if (user != null){
request.getSession().setAttribute("user",user);
response.sendRedirect(request.getContextPath()+"/info.jsp");
}else {
request.setAttribute("error","用户名密码不正确");
request.getRequestDispatcher("login.jsp").forward(request,response);
}
}
}
监听器
package com.openlab.listen;
import com.openlab.pojo.User;
import com.openlab.utils.TokenUtil;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.HashMap;
@WebListener
public class LoginStateListen implements HttpSessionAttributeListener {
private static HashMap<String,HttpSession> map = new HashMap<>();
@Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
HttpSession session = httpSessionBindingEvent.getSession();//当前添加用户的session
String name = httpSessionBindingEvent.getName();
if ("user".equals(name)){
User user = (User)httpSessionBindingEvent.getValue();
String token = TokenUtil.getToken(user);
if (map.containsKey(token)){//说明之前已经登录了
HttpSession oldSession = map.get(token);
oldSession.removeAttribute("user");
map.put(token,session);
}else {
//之前没有登录
map.put(token,session);
}
}else {
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
HttpSession session = httpSessionBindingEvent.getSession();
String name = httpSessionBindingEvent.getName();
if ("user".equals(name)){
//是需要删除user
String token = TokenUtil.getToken((User) httpSessionBindingEvent.getValue());
map.remove(token);
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
String name = httpSessionBindingEvent.getName();
if ("user".equals(name)){
HttpSession session = httpSessionBindingEvent.getSession();
User user = (User) httpSessionBindingEvent.getValue();//以前的值
String oldToken = TokenUtil.getToken(user);
map.remove(oldToken);//移除以前的记录
User newUser = (User) session.getAttribute("user");
String newToken = TokenUtil.getToken(newUser);
map.put(newToken,session);
}
}
}
前端页面
<%--
Created by IntelliJ IDEA.
User: 上杉
Date: 2020/8/11
Time: 15:00
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>用户登录</h3>
<form action="${pageContext.request.contextPath}/login.do" method="post">
<label for="userName">用户名</label><input type="text" id="userName" name="userName" ><br/>
<label for="password">密 码</label><input type="password" id="password" name="password"><br>
<p style="color: red">${error}</p>
<input type="submit" value="登录">
</form>
</body>
</html>
<%@ page import="java.util.Date" %><%--
Created by IntelliJ IDEA.
User: 上杉
Date: 2020/8/11
Time: 15:23
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--每三秒检测一次登录状态--%>
<script>setTimeout("location=location;",3000)</script>
<c:if test="${user!=null}">
<h1>恭喜您成功登录</h1>
<p>${user}</p>
</c:if>
<c:if test="${user==null}">
您的账号已经在别处登录,已经强制下线<br>
<a href="login.jsp">返回登录</a>
</c:if>
</body>
</html>
结果测试
我们先在谷歌浏览器打开登录页面
登录成功的页面
接下来我们再从火狐浏览器进行登录
登录成功后,我们返回谷歌浏览器查看