一.任务描述

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

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

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

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

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

二.效果演示

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


java如何防止点击两次问题 java限制同一用户重复点击_tomcat

谷歌浏览器


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


java如何防止点击两次问题 java限制同一用户重复点击_tomcat_02

谷歌浏览器


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


java如何防止点击两次问题 java限制同一用户重复点击_java如何防止点击两次问题_03

谷歌浏览器


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


java如何防止点击两次问题 java限制同一用户重复点击_java如何防止点击两次问题_04

edge浏览器


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


java如何防止点击两次问题 java限制同一用户重复点击_maven_05

edge浏览器


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


java如何防止点击两次问题 java限制同一用户重复点击_java-ee_06

谷歌浏览器


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

java如何防止点击两次问题 java限制同一用户重复点击_maven_07

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

java如何防止点击两次问题 java限制同一用户重复点击_maven_08

 

java如何防止点击两次问题 java限制同一用户重复点击_java_09

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

 

java如何防止点击两次问题 java限制同一用户重复点击_java_10

 很简单吧!

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

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

java如何防止点击两次问题 java限制同一用户重复点击_maven_11

 

java如何防止点击两次问题 java限制同一用户重复点击_java如何防止点击两次问题_12

三.代码实现

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

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的请求进行过滤:

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>

这是我第一个博客,可能这次写得并不好,但以后我还有很多机会,我会继续努力,坚持越写越好的。