java使用过滤器和监听器防止用户重复登录

简介: java使用过滤器和监听器防止用户重复登录

一.任务描述


相信很多小伙伴都使用QQ聊天工具,那是否遇到过这样的场景呢?当在一台电脑上已经登录QQ,此时因为某些原因需要在另一台电脑再登录相同号码的QQ,登录成功后会发现之前电脑上的QQ下线了。这就是QQ限制了同一个号码在电脑上不能重复登录,我们的Web程序也可以进行重复登录的限制,那么本次任务就是用过滤器和监听器来解决重复登录问题。具体任务如下:


1、未登录时不能访问主界面。


2、登录后,登录信息存储到session中。


3、监听器监听session属性值变化。


4、一个浏览器中已经登录,如果在另一个浏览器中重复登录,则清除前次登录信息。


二.效果演示


1.运行web应用程序,进入谷歌浏览器登录界面


eb4aae7904214d6aba2750e5216ef84a.png


2.此时为第一次进入程序,输入一个用户名密码。(这里输入用户名为haiexijun)


db303912c5214365bfcc397d0ab91222.png


3.点击提交按钮登录,显示登录成功。


79de3e2dc9b84eaab3865b2bc7787d48.png


4.我如果用另外一个客户端登录,模拟异地登陆。上面第一次用的是谷歌浏览器,这次用edge浏览器输入用户名。


f26fd335683a4a0a8c169696922bd1e2.png


5.在edge浏览器上点击提交,则会在edge上成功登陆.


ac377cd758824802aea795cb8a4931e4.png


6.返回谷歌浏览器,刷新登陆界面后。会显示账号被异地登录了,并要求重新登录了。


936f128a674242adb5150e850915a350.png


7.点击确定会让你重新登录


39c2123959ad42298bc6d7ab22fd33e1.png


8.并且可以在谷歌浏览器重新登录,并成功登陆


a84c6779f5a04d1ca4f0707c3e96fee9.png

d741994c071d4071bb9b2f2504bdf185.png


9.然后再一次回到edge浏览器再刷新则会被提醒账号被异地登录,并提醒重新登陆:


cf920313a9db4030b62a7a0e6153d742.png


很简单吧!


10.之前相同用户名异端登录提醒的功能算是实现了,最后测试一下不同用户名则不会出现提示。


在edge浏览器输入用户名为zcbad,和谷歌浏览器的haiexijun不是一个用户了,回到谷歌浏览器刷新则不会出现异端登录的提醒。完美实现!


ced1673b41d242e2b8e4dfb1275e2dad.png

4ced84b117d54290b0b9e62e621b1aab.png


三.代码实现


1.在idea中用maven创建一个webapp项目,项目结构如图:


2378171c8bc547c585ff44056bb5be83.png


2.然后在webapp目录下创建一个login.html的用户登录界面:


<!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处理请求。

3.设置一个过滤器loginFilter,对url为/login的请求进行过滤:

1.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,则为同一个客户端同一个用户登录。否则异地登录,则刷新就要重新登陆。


4.同时,还要写一个监听器sessionListener:


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。


5.login.java的servlet:


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页面了,此时就登录成功了!


6.index.html代码:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>登陆成功!</h1>
</body>
</html>


7.异地登录时跳转到logout.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>


8.web.xml的配置:


<!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>


其他配置:


9.maven依赖配置:


<?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>
相关文章
|
14天前
|
分布式计算 Java Hadoop
Hadoop-30 ZooKeeper集群 JavaAPI 客户端 POM Java操作ZK 监听节点 监听数据变化 创建节点 删除节点
Hadoop-30 ZooKeeper集群 JavaAPI 客户端 POM Java操作ZK 监听节点 监听数据变化 创建节点 删除节点
36 1
|
15天前
|
Java
Java 登录输入的验证码
Java 登录输入的验证码
17 1
|
12天前
|
Java C#
Java的监听处理事件--小球移动案例
Java的监听处理事件--小球移动案例
9 0
|
2月前
|
存储 算法 Java
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
这篇文章详细介绍了在Java项目中如何使用MD5算法对用户密码进行加密存储和登录验证,包括加入依赖、编写MD5工具类、注册时的密码加密和登录时的密码验证等步骤,并通过示例代码和数据库存储信息展示了测试效果。
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
|
1月前
|
监控 前端开发 Java
Java里的过滤器和拦截器是什么原理,如何选择?
Java里的过滤器和拦截器是什么原理,如何选择?
21 0
|
2月前
|
SQL Java 数据库连接
【Azure 应用服务】Java ODBC代码中,启用 Managed Identity 登录 SQL Server 报错 Managed Identity authentication is not available
【Azure 应用服务】Java ODBC代码中,启用 Managed Identity 登录 SQL Server 报错 Managed Identity authentication is not available
|
3月前
|
关系型数据库 MySQL Java
|
3月前
|
存储 程序员
JavaWeb之Listener监听器
JavaWeb之Listener监听器
63 0
|
4月前
|
安全 Java
使用FilterChain实现Java中的过滤器链
使用FilterChain实现Java中的过滤器链
|
4月前
|
存储 NoSQL Java
Redis系列学习文章分享---第三篇(Redis快速入门之Java客户端--短信登录+session+验证码+拦截器+登录刷新)
Redis系列学习文章分享---第三篇(Redis快速入门之Java客户端--短信登录+session+验证码+拦截器+登录刷新)
84 0