Java Web开发防止多用户重复登录的完美解决方案_java

目前web项目中,很多情况都是可以让同一个账户信息在不同的登录入口登录这次,这样子就不那么美好了。

推荐阅读:

Java 多用户登录限制的实现方法

现在有两种解决方案:

1、将用户的登录信息用一个标志位的字段保存起来,每次登录成功就标记1,注销登录就标记为0,当标记为1的时候不允许别人登录。

2、将用户的登录信息保存在application内置作用域内, 然后利用session监听器监听每一个登录用户的登录情况。

很显然,第一种方式 每次登录 都需要操作数据库,多了一些不必要的性能开销,而且在登录状态下 万一突然电脑关闭了,那就永远都不能登录了,可用性比较低。

但是第二种方式就不一样了,可操作性强,很方便维护所有在线用户的信息。

接下来 主要介绍第二种方式的具体实现:

1、在处理登录的login方法中,先查询数据库验证下该用户是否存在,如果存在 判断该登录账户是否已经锁定了, 然后从application内置作用域对象中取出所有的登录信息,查看该username账户是否已经登录,如果登录了,就友好提示下,反之表示可以登录,将该登录信息以键值对的方式保存在application中。

代码如下:

//没有使用零配置前 每个访问的方法都要加上@Action ,否则404
@Action(value="login", results={
@Result(name="index", location="index.jsp"),
})
public String login() throws Exception {
try{
User result = userService.login(user.getFuUserName(), user.getFuPassword());
if(result!=null){
if(result.getFuStatus()!=null && result.getFuStatus()==0){
super.setRequestAttr(Constant.MESSAGE, "抱歉,该用户已被锁定!");
return "error";
}
Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
boolean isExist = false;
String sessionId = super.getSessionId(false);
if(loginUserMap==null){
loginUserMap = new HashMap<String, String>();
}
for (String username : loginUserMap.keySet()) {
//判断是否已经保存该登录用户的信息 或者 如果是同一个用户进行重复登录那么允许登录
if(!username.equals(result.getFuUserName()) || loginUserMap.containsValue(sessionId)){
continue;
}
isExist = true;
break;
}
if(isExist){
super.setRequestAttr(Constant.MESSAGE, "抱歉,该用户已登录!");
return "error";
}else {
loginUserMap.put(result.getFuUserName(), sessionId);
}
//登录成功
super.setSessionAttr(Constant.LOGIN_USER, result);
super.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);
logger.info(result.getFuUserName() + " 登录成功!");
//如果 session中fromUrl有值,就跳转到该页面
String fromUrl = (String)super.getSessionAttr(Constant.FROM_URL);
if(fromUrl!=null){
super.setSessionAttr(Constant.FROM_URL, null);
super.getResponse().sendRedirect(fromUrl.toString());
return null;
}
return "index";
}
}
catch (Exception e) {
e.printStackTrace();
logger.info("登录失败: "+e.getMessage());
}
super.setRequestAttr("message", "用户名或密码错误");
return "error";
}

2、登录入口处理完之后,考虑到会话结束的话,那么对应的登录用户也应该相应的注销登录。我们可以写一个Session监听器,监听sessioon销毁的时候,我们将登录的用户注销掉,也就是从application中移除。表示该用户已经下线了。

代码如下:

package com.facelook.util;
import java.util.Map;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.apache.log4j.Logger;
import com.facelook.entity.User;
public class SessionListener implements HttpSessionListener{
private Logger logger = Logger.getLogger(this.getClass());
@Override
public void sessionCreated(HttpSessionEvent event) {
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
//在session销毁的时候 把loginUserMap中保存的键值对清除
User user = (User)event.getSession().getAttribute("loginUser");
if(user!=null){
Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute("loginUserMap");
loginUserMap.remove(user.getFuUserName());
event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);
}
}
}

web.xml中配置如下:

<!-- session listener -->
<listener>
<listener-class>com.facelook.util.SessionListener</listener-class>
</listener>

3、另外,还有一个问题,如果说登录的用户突然关闭了浏览器或者页面而没有点击退出按钮。那么可以利用beforeunload 事件,在浏览器刷新或者关闭的时候触发。

//在刷新或关闭时调用的事件
$(window).bind('beforeunload',function(){
$.ajax({
url:"${ctx}/system/user/user!logout.action",
type:"post",
success:function(){
alert("您已退出登录");
}
});
);

但是如果一些客观原因,比如电脑突然关机,自动重启,等等,这些就没法避免了,所以只能等待服务器端的session会话重置之后才可以再登录。

除非 做一个 统计所有在线人员的模块,管理员在里面进行在线人员的登录登出的状态管理,把那些有问题的登录用户直接销毁掉。

接下来简单介绍下在线人员模块的管理:

1、首先需要一个session监听器来监听所有的回话create的情况,这时候每次创建一个session就可以count+1 ,然后销毁的时候count-1 ,另外还需要一个ServletContext的监听器来监听web应用的生命周期,获取servletContext对象,然后将在线人员总数统计出来存放进去;

具体代码如下:

package com.facelook.util;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.apache.log4j.Logger;
import com.facelook.entity.User;
public class SessionListener implements HttpSessionListener,ServletContextListener{
private int count;
private ServletContext servletContext = null;
public SessionListener() {
count = 0;
}
private Logger logger = Logger.getLogger(this.getClass());
@Override
public void sessionCreated(HttpSessionEvent event) {
count++;
setContext(event);
logger.info("***************the http session is created...***************");
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
//在session销毁的时候 把loginUserMap中保存的键值对清除
User user = (User)event.getSession().getAttribute("loginUser");
if(user!=null){
Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute("loginUserMap");
loginUserMap.remove(user.getFuUserName());
event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);
}
count--;
setContext(event);
logger.info("***************the http session is destroyed...***************");
}
public void setContext(HttpSessionEvent httpSessionEvent){
httpSessionEvent.getSession().getServletContext().setAttribute("online", count);
}
@Override
public void contextDestroyed(ServletContextEvent servletcontextevent) {
this.servletContext = null;
logger.info("***************the servlet context is destroyed...***************");
}
@Override
public void contextInitialized(ServletContextEvent servletcontextevent) {
this.servletContext = servletcontextevent.getServletContext();
logger.info("***************the servlet context is initialized...***************");
}
}

2、在UserAction中创建管理在线用户的模块的方法,并且支持强制退出的功能;

/** * 退出登录
* @return
* @throws ServletException
* @throws IOException
*/
public String logout() throws ServletException, IOException{
try {
Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
User user = (User) super.getSessionAttr(Constant.LOGIN_USER);
super.removeAttribute(Constant.LOGIN_USER_MAP);
loginUserMap.remove(user.getFuUserName());
super.setApplicationAttr(Constant.LOGIN_USER_MAP,loginUserMap);
logger.info("退出登录成功!");
} catch (Exception e) {
e.printStackTrace();
logger.error("退出登录失败: "+e.getMessage());
}
return INPUT;
}
/**
* 在线用户管理
* @return
*/
public String loginManager(){
return SUCCESS;
}
/**
* 强制退出其他用户
* @return
*/
public String logoutOther(){
try {
String username = ServletActionContext.getRequest().getParameter("username");
Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
if(username!=null && loginUserMap.containsKey(username)){
loginUserMap.remove(username);
super.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);
}
} catch (Exception e) {
e.printStackTrace();
logger.info("强制退出失败: "+e.getMessage());
}
return null;
}

3、在管理页面加载在线用户的列表;

对应的方法定义完毕之后,然后再在对应的管理页面添加在线列表,具体如下:

<%@page import="java.util.Map"%>
<%@page import="java.util.Map.Entry"%>
<%@ page language="java" pageEncoding="UTF-8" %>
<%@ include file="/common/taglib.jsp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>欢迎来到Facelook</title>
<%@ include file="/common/resource.jsp" %>
<script type="text/javascript">
<!--
//在刷新或关闭时调用的事件
$(window).bind('beforeunload',function(){
$.ajax({
url:"${ctx}/system/user/user!logout.action",
type:"post",
success:function(){
alert("您已退出登录");
}
});
});
function logout(username){
if(username=="${sessionScope.loginUser.fuUserName}"){
alert("不允许退出自己账号!");
return;
}
$.ajax({
url:"${ctx}/system/user/user!logoutOther.action?username="+username,
type:"post",
success:function(){
$("#tr"+username).hide();
var count = parseInt($("#count").html());
$("#count").html(count-1);
alert("退出成功!");
}
});
}
//-->
</script>
</head>
<body>
<%@ include file="/common/header.jsp" %>
<div id="main" class="wrap">
<%@ include file="/common/lefter.jsp" %>
<div class="righter">
<div class="main">
<h2>登录列表</h2>
<%
Map<String,String> map = (Map<String,String>)application.getAttribute("loginUserMap");
out.println("目前共有<font id='count'>"+map.size()+"</font>个用户在线!!");
%>
<table border="1" width="400">
<%for(Entry<String,String> m : map.entrySet()){%>
<tr id="tr<%=m.getKey()%>">
<td>
<%=m.getKey()%>
</td>
<td width="80">
<a href="javascript:logout('<%=m.getKey()%>')">强制退出</a>
</td>
</tr>
<%}%>
</table>
</div>
</div>
</div>
<%@ include file="/common/footer.jsp" %>
<%@ include file="/common/message.jsp" %>
</body>
</html>

好了启动部署项目,然后启动服务,进入在线用户管理模块,简单效果如下图:

需要注意的是:当前登录用户 不允许强制退出自己的登录信息。

这样子,基本上可以实现防止多用户登录的案例了!

以上所述是小编给大家介绍的Java Web开发防止多用户重复登录的完美解决方案,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
web防止重复登录
php 防止多用户登录、渝税通多用户解决方案、防止表单重复提交、防止重复提交、js防止重复点击,以便于您获取更多的相关知识。

时间: 2024-10-21 14:38:26

Java Web开发防止多用户重复登录的完美解决方案_java的相关文章

Java Web开发项目中中文乱码解决方法汇总_java

Java Web项目中,解决中文乱码方法总结如下 第一种情况:调用jsp页面中文显示乱码问题描述:通过浏览器调用jsp页面,在浏览器中显示的中文内容出现乱码. 解决方法:首先确认本jsp在编辑器中保存文件内容时,使用的是utf-8的编码格式,然后在jsp页面的开始处添加<%@ pageEncoding="utf-8"%>就可以解决这种中文乱码问题 第二种情况:调用servlet页面显示乱码问题描述:通过浏览器调用servlet,servlet在浏览器中显示的内容出现乱码.

Tomcat与Java Web开发技术详解连载之二

web|详解 2.2.4 部署HTML文件 在helloapp目录下加入index.htm文件,这个文件仅仅用来显示一串带链接的字符"Welcome to HelloApp", 它链接到login.jsp文件.以下是index.htm文件的代码: <html><head><title>helloapp</title></head><body ><p><font size="7"

初学者的忧伤-java/web开发同一账户同一时间只能登陆一次

问题描述 java/web开发同一账户同一时间只能登陆一次 我们的项目需要实现一个用户只在同一时间只能有一个登陆,后面人如果登陆则提示他已经此用户已经登陆,保证第一个人登陆不掉线,谁会写啊,最好有可用的源码,谢谢了! 解决方案 一般都是后面的踢掉前面的.你的设计有致命的问题就是,如果你的前一个用户掉线了,或者突然关机了. 那么他再登录算新用户了吧(可能ip变化了),那么系统还得保证那个根本不存在的用户在线,而不让登录,岂不是这个用户永远登录不了了. 解决方案二: 当然你非要这么写也不是不可以,很

新书出版:Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Sp

本文为原创,如需转载,请注明作者和出处,谢谢! 作者:李宁 图书详细信息: ISBN:9787302195757 定价:79.8元 印次:1-1 装帧:平装 印刷日期:2009-4-23   图书简介: SSH是目前最流行的Java Web开发技术.本书通过对SSH中的各种技术循序渐进地讲解,使读者尽快掌握开发基于SSH的Web程序的方法.本书内 容包括Web客户端技术.JSP/Servlet技术.Struts 2(拦截器.类型转换.输入校验.上传和下载文件.Struts 2的各种标签.对 AJ

新书出版:Java Web开发技术大全——JSP+Servlet+Struts 2+Hibernate+Spring+Ajax (附源代码)

本文为原创,如需转载,请注明作者和出处,谢谢! 源代码下载 作者:李宁 图书详细信息: ISBN:9787302195757 定价:79.8元 印次:1-1 装帧:平装 印刷日期:2009-4-23 图书简介: SSH是目前最流行的Java Web开发技术.本书通过对SSH中的各种技术循序渐进地讲解,使读者尽快掌握开发基于SSH的Web程序的方法.本书内 容包括Web客户端技术.JSP/Servlet技术.Struts 2(拦截器.类型转换.输入校验.上传和下载文件.Struts 2的各种标签.

Java Web开发模式

一 Java Web开发模式的变迁 1 最初的Java web服务器端编程技术是Servlet,利用Servlet就可以开发出一个Web应用程序. 2 为了解决Servlet缺陷,SUN推出了JSP技术.但是开发人员又走向了另一个极端就是完全放弃了Servlet.   在JSP页面混合使用HTML标记和java代码编写的脚本元素标记来开发Web程序.采用这种方法虽然可以编写JSP页面变得简单,直观,然而,他只适合于业务流程简单,系统规模较小的应用系统.   如果系统较大的话,就会出现两个严重的缺

Tomcat与Java Web开发技术详解连载之一

web|详解 本章介绍如何在Tomcat上创建和发布Web应用.这里首先讲解Tomcat的目录结构以及Web应用的目录结构,接着介绍如何将HTML.Servlet.JSP和Tag Library部署到Web应用中,然后介绍把整个Web应用打包并发布的方法,最后介绍如何在Tomcat上配置虚拟主机. 本章侧重于讨论Web应用的结构和发布方法,所以没有对本章的Servlet和JSP的例子进行详细解释,关于Servlet和JSP的技术可以分别参考其它章节的内容. 2.1 Tomcat的目录结构 在To

Tomcat与Java Web开发技术详解连载之三

web|详解 2.2.8 创建并发布WAR文件 Tomcat既可以运行采用开放式目录结构的Web应用,也可以运行WAR文件.在本书配套光盘的sourcecode/chapter2/helloapp目录下提供了所有源文件,只要把整个helloapp目录拷贝到/webapps目录下,即可运行开放式目录结构的helloapp应用.在Web应用的开发阶段,为了便于调试,通常采用开放式的目录结构来发布Web应用,这样可以方便地更新或替换文件.如果开发完毕,进入产品发布阶段,应该将整个Web应用打包为WAR

用Stripes做Java Web开发

摘要:Stripes是一个以让程序员的web开发简单而高效为准则来设计的基于动作的开源Java web框架.本文将介绍Stripes与其它如Struts之类基于动作的框架的区别和其提供的一些存在于Ruby on Rails之中的简单性. Stripes是一个以让程序员的web开发简单而高效为准则来设计的基于动作的开源Java web框架.传统的Java web开发着眼于借去耦(Decoupling)来实现其灵活性,但导致多个的配置文件,额外的对象,和其他资源的分散.这些困难造成相当多的程序员的更