实现了Java web开发账号单一登录的功能,防止同一账号重复登录,后面登录的踢掉前面登录的,使用过滤器Filter实现的。可以先下载项目下来测试下效果。
有博客写的是没个一段时间(比如500ms)读取后台的session进行验证,这种方法除了会占用资源,还会出现访问session(请求1)的返回值和自己提交请求(请求2)的返回值发生冲突。比如请求1先提交,此时请求1的返回值还未返回到前端,请求2提交,实际上我们想要的是请求1的返回值先返回,然后再返回请求2的返回值,但是这不是肯定会发生的,ajax的机制所导致的,具体什么原因没查到。总之出现了请求2先返回了返回值,这时候请求1接收到了请求2的返回值,妥妥的前端出现错误,但是后台却是成功的。
还有写的使用seesion监听器来实现,但是session过滤器只能用来监听session的改变(创建、修改、销毁等),如果有重复用户登录,只能保证清除掉前面用户的session,是无法通过监听器来控制前端进行页面跳转等操作的。
下面进入主题
工程下载链接:https://pan.baidu.com/s/1FgxLz62t5FAwl3T6Jq3CLw&shfl=sharepset 链接 提取码:zngv

其中:jquery-1.11.3.js是网上的工具

java实现禁止多设备登录 java防止重复登录_Java

建立两个简单的界面

登录界面:为了简单没有设置密码,直接输入账号点击登录就行

java实现禁止多设备登录 java防止重复登录_Java_02

// index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
  </head>
  
  <body>
    <input id="username" name="username" type="text">
    <!-- <a href="singlecount.jsp" target="_self"> -->
    	<button id="btnlogin" name="btnlogin">登录</button><!-- </a> -->
    	
    <!-- 引入jQuery -->
    <script type="text/javascript" src="js/jquery-1.11.3.js"></script>
    <script type="text/javascript" src="js/jsSubmit.js"></script>
  </body>
</html>

主页面:简单的一个提交按钮

java实现禁止多设备登录 java防止重复登录_单一账号登录_03

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'SingleCount.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
      已登录. <br>
    <button id="btnsubmit" name="submit">提交</button>
    
    <!-- 引入jQuery -->
    <script type="text/javascript" src="js/jquery-1.11.3.js"></script>
    <script type="text/javascript" src="js/jsSubmit.js"></script>
  </body>
</html>

写ajax,向后台提交请求

// jsSubmit.js


$(document).ready(function() {
	// 登录按钮
	$("#btnlogin").click(function() {
		//data,dataType,type,url
		$.ajax({
			url: 'LoginServlet?method=login',
			type: 'post',
			data: {username: $("input[name='username']").val()},  // 将用户名传给servlet
			//dataType:'json',
			success: function(msg) {  // msg为从servlet接收到的返回值
				if (msg == 1) {  // 接收到后台数据为1,正常登录
					window.location.href = "singlecount.jsp";
				}		
			},
			error:function(){
				window.alert("错误!");
			}
		});
	});
	// 提交按钮
	$("#btnsubmit").click(function() {
		//data,dataType,type,url
		$.ajax({
			url: 'SubmitServlet?method=submit',
			type: 'post',
			//dataType:'json',
			success: function(msg) {  // msg为从servlet接收到的返回值
				if (msg >= 1) {  // 正常
					window.alert("提交总数" + msg);
				}		
			},
			error:function(jqXHR){
				if(jqXHR.status == 900){  // 900状态码
					window.alert("登录状态失效,请重新登录!");
					window.location.href = "/OneLogin";
				}
			}
		});
	});
});

servlet

这部分有点长,其实主要内容直接看doPost方法就可以了。

// LoginServlet

package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
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 javax.servlet.http.HttpSession;

@SuppressWarnings("serial")
//注解表明什么样的情况下可以访问该内容
@WebServlet(urlPatterns={"/LoginServlet"})
public class LoginServlet extends HttpServlet {

	private PrintWriter out; // 输出流
	private String user;
	private String method;
	private HttpSession session;
	// 建立一个Map存储session信息,key-用户名,value-session
	public static Map<String, HttpSession> user_Session = new HashMap<String, HttpSession>();
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		super.init(config);
	}

	@Override
	protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doDelete(req, resp);
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doPost(req, resp);
	}

	@Override
	// 在这里实现方法
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		resp.setContentType("text/html");
		//语言编码
		req.setCharacterEncoding("utf-8");
		resp.setCharacterEncoding("utf-8");
		out = resp.getWriter();
		
		user = req.getParameter("username");  // 获取index界面username的内容
		method = req.getParameter("method");  // 获取方法名
		session = req.getSession();  // 获取session
		switch (method) {
		case "login":
			mLogin();
			break;
		default:
			break;
		}
		out.flush();
		out.close();
		
	}

	private void mLogin() {  // 按登录按钮调用的方法
		// TODO Auto-generated method stub
		removeUser(user);
		session.setAttribute("name", user);
		user_Session.put(user, session);  // 新增或覆盖session
		System.out.println(user_Session);
		out.println(1); // 返回值1,随意选个值,和前端对应就可以
	}
	
	/**
	 *  判断是否有重复用户,
	 *  若出现重复用户,踢掉前面登录的用户,即删除其session
	 */
	private void removeUser(String user) {
		if(user_Session.containsKey(user))
			user_Session.get(user).invalidate();
	}
}
// SubmitServlet

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
//注解表明什么样的情况下可以访问该内容  会在js和web.xml中使用
@WebServlet(urlPatterns={"/SubmitServlet"})
public class SubmitServlet extends HttpServlet {
	
	private PrintWriter out; // 输出流
	private String method;
	private int number = 0;  // 计数

	@Override
	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		super.init(config);
	}

	@Override
	protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doDelete(req, resp);
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doPost(req, resp);
	}

	@Override
	// 在这里实现方法
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		resp.setContentType("text/html");
		//语言编码
		req.setCharacterEncoding("utf-8");
		resp.setCharacterEncoding("utf-8");
		out = resp.getWriter();
		
		method = req.getParameter("method");  // 获取方法名
		switch (method) {
		case "submit":
			mSubmit();
			break;
		default:
			break;
		}
		out.flush();
		out.close();
	}
	
	private void mSubmit() {  // 按提交按钮调用的方法
		// TODO Auto-generated method stub
		number++;
		out.println(number);
	}
}

过滤器

过滤器的原理这里就不说了,简单来说就是请求要先经过过滤器才能到达servlet,也就是说如果请求不满足要求就无法通过过滤器,这里的要求是要有session。

package filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebFilter("/SessionFilter")
public class SessionFilter implements Filter {

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
			throws IOException, ServletException {
		// TODO Auto-generated method stub
		HttpServletRequest request = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
        String strURL = request.getRequestURL().toString();  // 获取请求路径
        // System.out.println(strURL);
        // 只过滤来自SubmitServlet请求和singlecount.jsp的加载,可以设置成自己想过滤的
        // 需要在web.xml中添加<filter>
        if(strURL.indexOf("SubmitServlet") != -1 || strURL.indexOf("singlecount.jsp") != -1){  
        	if(request.getSession().getAttribute("name") == null){
    			request.getSession().invalidate();
    			response.sendError(900, "登录失效,请重新登录!");  // 自定义状态码,session失效
    			// 900 到ajax的error中处理
    			return;
        	}
        	else {
        		arg2.doFilter(arg0, arg1);
        	}
        }
        else {
        	arg2.doFilter(arg0, arg1);
        }
    }

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
		
	}

}

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>OneLogin</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

  <filter>
    <filter-name>filter.SessionFilter</filter-name>
    <filter-class>filter.SessionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>filter.SessionFilter</filter-name>  
    <url-pattern>/singlecount.jsp</url-pattern>  <!-- 给界面添加过滤器 -->
  </filter-mapping>
  <filter-mapping>
    <filter-name>filter.SessionFilter</filter-name>
    <url-pattern>/SubmitServlet</url-pattern>  <!-- 给servlet添加过滤器 -->
  </filter-mapping>
</web-app>

实现效果

可以使用两个不同的浏览器当两个客户端,或者电脑多就用多台电脑。

相同账号登录时,前面账号再点提交请求就会给出提示,跳转到登录界面

java实现禁止多设备登录 java防止重复登录_java实现禁止多设备登录_04


未登录直接进入:http://localhost:8080/OneLogin/singlecount.jsp

java实现禁止多设备登录 java防止重复登录_java实现禁止多设备登录_05


如果也想实现跳转效果,在jsSubmit.js的$(document).ready(function() {…}); 前面加入是否有session的判断,没有就给出提示,跳转到登录界面。

以上,欢迎大佬们批评指正。