相信很多小伙伴都使用QQ聊天工具,那是否遇到过这样的场景呢?当在一台电脑上已经登录QQ,此时因为某些原因需要在另一台电脑再登录相同号码的QQ,登录成功后会发现之前电脑上的QQ下线了。这就是QQ限制了同一个号码在电脑上不能重复登录,我们的Web程序也可以进行重复登录的限制,那么本次任务就是用过滤器和监听器来解决重复登录问题。具体任务如下:
1、未登录时不能访问主界面。
2、登录后,登录信息存储到session中。
3、监听器监听session属性值变化。
4、一个浏览器中已经登录,如果在另一个浏览器中重复登录,则清除前次登录信息。
1.运行web应用程序,进入谷歌浏览器登录界面
谷歌浏览器
2.此时为第一次进入程序,输入一个用户名密码。(这里输入用户名为haiexijun)
谷歌浏览器
3.点击提交按钮登录,显示登录成功。
谷歌浏览器
4.我如果用另外一个客户端登录,模拟异地登陆。上面第一次用的是谷歌浏览器,这次用edge浏览器输入用户名。
edge浏览器
5.在edge浏览器上点击提交,则会在edge上成功登陆.
edge浏览器
6.返回谷歌浏览器,刷新登陆界面后。会显示账号被异地登录了,并要求重新登录了。
谷歌浏览器
7.点击确定会让你重新登录
8.并且可以在谷歌浏览器重新登录,并成功登陆
9.然后再一次回到edge浏览器再刷新则会被提醒账号被异地登录,并提醒重新登陆:
很简单吧!
10.之前相同用户名异端登录提醒的功能算是实现了,最后测试一下不同用户名则不会出现提示。
在edge浏览器输入用户名为zcbad,和谷歌浏览器的haiexijun不是一个用户了,回到谷歌浏览器刷新则不会出现异端登录的提醒。完美实现!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form method="post" action="/login">
用户名:<input type="text" name="username"/>
密码:<input type="password" name="password"/>
<input type="submit" name="登录"/>
</form>
</body>
</html>
用post请求传到的名为login的servlet处理请求。
package org.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@WebFilter(filterName = "loginFilter",urlPatterns = "/login")
public class loginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//获取httpServletRequest对象
HttpServletRequest request=(HttpServletRequest) servletRequest;
//获取全局对象
ServletContext context=request.getServletContext();
//获取userName
String userName=request.getParameter("username");
//设置一个用户列表,用于记录用户登录
if (context.getAttribute("userList")==null){
//如果第一次登录这个客户端,就创建列表,加入用户
List<String> userList = new ArrayList<String>();
userList.add(userName);
context.setAttribute("userList",userList);
}else {
//如果不是第一次登录
List<String> userList= (List<String>) context.getAttribute("userList");
//就判断用户列表中是否有此用户
if (!userList.contains(userName)){
//如果不包含该用户,就添加进去
userList.add(userName);
}
}
//获取此客户端的session
//session列表
HttpSession session= request.getSession();
if (context.getAttribute("sessionMap")==null){
Map<String,HttpSession> sessionMap=new HashMap<String,HttpSession>();
sessionMap.put(userName,session);
context.setAttribute("sessionMap",sessionMap);
}else {
Map<String,HttpSession> sessionMap= (Map<String, HttpSession>) context.getAttribute("sessionMap");
if (!sessionMap.containsKey(userName)){
sessionMap.put(userName,session);
}
//测试sessionMap
System.out.println("======sessionMap======");
for (Map.Entry<String,HttpSession> entry:sessionMap.entrySet()){
System.out.println(entry.getKey()+":"+entry.getValue());
}
System.out.println("=======================");
}
//给session设置username
session.setAttribute("userName",userName);
//判断是否为同一个session
Map<String,HttpSession> sessionMap = (Map<String, HttpSession>) context.getAttribute("sessionMap");
HttpSession session1=sessionMap.get(userName);
if (session1==session){
filterChain.doFilter(servletRequest,servletResponse);
}else {
HttpServletResponse response=(HttpServletResponse) servletResponse;
response.sendRedirect("/logout.html");
//用于销毁session
session.invalidate();
}
}
}
我先把用户名加入一个arraylist列表中,其实这一步可有可无啦(一开始写的,忘了删)。创建名为sessionMap的map<String,HttpSession>集合,把每次登录所创建的不同session存进去,键为userName,值为当前应用的session。以便后续监听和判断。
网上很多人是通过sessionid来判断是否是同一个客户端上的登录,但我直接比较不同客户端登录时服务器创建的session是否为同一个对象(不同客户端登录,服务器创建的session就是不同的,直接比较是否为同一个httpsession对象就行了)。
如果判断当前session和sessionMap中保存的同用户名的session为同一个session,则为同一个客户端同一个用户登录。否则异地登录,则刷新就要重新登陆。
package org.example.listener;
import javax.servlet.ServletContext;
import javax.servlet.http.*;
import java.util.Map;
public class sessionListener implements HttpSessionAttributeListener, HttpSessionListener {
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
//获取session
HttpSession session=event.getSession();
//通过session来获取context上下文对象
ServletContext context=session.getServletContext();
//获得用户名
String userName= (String) session.getAttribute("userName");
//判断sessionId是否于之前登录时sessionMap里存的相同
Map<String,HttpSession> sessionMap = (Map<String, HttpSession>) context.getAttribute("sessionMap");
String sessionId=sessionMap.get(userName).getId();
if (!session.getId().equals(sessionId)){
sessionMap.remove(userName);
}
sessionMap.put(userName,session);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("destory");
}
}
这个监听器的作用是监听session的属性的变化,在session属性发生改变时触发该监听器。第一次开启应用时会触发一次。后来每在已登录的客户端以外的客户端上登录也会产生新的session,也就是会有session的属性被设置,从而也触发监听器,,进行判断sessionid,然后更改sessionMap。
package org.example.servlet;
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("/login")
public class login extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/index.html").forward(req,resp);
}
}
通过了过滤器后,到login.java的servlet这里,这一步也就是简简单单的把请求转发到index.html页面了,此时就登录成功了!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>登陆成功!</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>logout</title>
</head>
<body>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>logout</title>
</head>
<body>
<script type="text/javascript">
alert("你尚未登录,或者账号在异地登陆,请重新登陆!");
window.location.href="http://localhost:8888/login.html";
</script>
</body>
</html>
</body>
</html>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>org.example.listener.sessionListener</listener-class>
</listener>
</web-app>
其他配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mylogin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mylogin Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>mylogin</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>