`
hbxflihua
  • 浏览: 678649 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Application级账号锁定及账号独立会话操作

 
阅读更多

 

Application用于存放应用程序级的共享数据,比如用户访问量统计、防止同一账号同一时间多客户端登录等等。一般而言,我们不建议在application中存放数据,尤其是大数据集合,在访问量比较大的网站有时甚至会产生严重的性能瓶颈。

 

这里仅就账号锁定和账号独立会话两个操作在application中的应用做一个简单的介绍,不妥之处,望不吝赐教。

 

账号锁定

同一账号连续N次(可配置)登录(login_count)密码有误,则锁定该账号。账号锁定后,该用户在持续锁定时间(lock_time)范围(比如24小时)内不允许登录。超过持续锁定时间后,再次登录,重新计算登录次数。拿网上银行系统举例(可能不太恰当)。网上银行系统一般都规定在一天内,密码连续三次输入错误,则该账号被锁定。

 

账号独立会话

类似于QQ的功能,一个账号只能在一个客户端(这里可以指一台电脑或者一个会话,不过原理差不来多少,本示例基于session)进行操作,采取后来居上,前客户端登录直接被踢的方式。可能提炼的不够恰当,暂且就这么叫吧,朋友们有更好的术语不妨留言讨论。当然也可以在登录时,判断该账号是否已登录,如果已经登录则给予提示。方法不尽相同,不过殊途同归。

 

我打算将这两个功能合在一起,主要是基于都是application级应用的考虑。下面谈谈具体的实现方案。

 

概要说明

这里需要记录下账号的相关信息,所以需要一个POJO类,需要一个拦截器来验证账号信息。

 

具体描述

在用户登录时,记录下账号信息如登录名、sessionId、最后登录时间、连续错误登录次数等等。以登录名为key,账号信息为value存放在Map集合中,并将Map置于application

 

用户每次登录都记录下登录时间,,登录错误则错误登录次数加一。连续错误登录,次数超过限制,则不允许继续登录。超出锁定时间后,再次登录时,连续错误登录次数清零,从而实现账号锁定的功能。

 

同一账号只会记录该账号最后一次登录的sessionId。在拦截器中对用户的会话ID进行验证,如果不一致,则为之前登录的客户端,直接将当前会话清除,以实现账号独立会话的功能。

 

具体示例代码如下:

 

import java.util.Date;


/**
 * 登录信息 
 * @remark 该信息保存在application中,主要用于登录锁定和同一账户同时只能登录一次
 * @author lihua
 * @version V1.0
 * @createDate 2012-9-28
 */
public final class LoginInApp {

	private String sessionId;//保存当前用户最新的sessionid
	private Date loginTime;//最后登录时间
	private int loginCount;//连续错误登录次数,该次数会在登录超过可连续登录时间间隔后自动回位到1
	
	public LoginInApp() {
	}
	
	public LoginInApp(String sessionId, Date loginTime, int loginCount) {
		this.sessionId = sessionId;
		this.loginTime = loginTime;
		this.loginCount = loginCount;
	}

	public String getSessionId() {
		return sessionId;
	}
	public void setSessionId(String sessionId) {
		this.sessionId = sessionId;
	}
	public Date getLoginTime() {
		return loginTime;
	}
	public void setLoginTime(Date loginTime) {
		this.loginTime = loginTime;
	}

	public int getLoginCount() {
		return loginCount;
	}

	public void setLoginCount(int loginCount) {
		this.loginCount = loginCount;
	}	
	
}

 

   账号锁定验证代码

 

/**
		 * 账号锁定验证
		 */
		String accountLockState = PropertiesUtil.getString(Constants.ACCOUNT_LOCK_STATE);//锁定状态开关
		if(ValidateUtil.matchString(accountLockState, "on")){	//开启锁定
			HttpSession session = request.getSession();
			Object obj = session.getServletContext().getAttribute(Constants.KEY_SESSION_MESSAGE);
			if(obj!=null){			//application 用户缓存不为空
				Map<String, LoginInApp> map = (HashMap<String, LoginInApp>)obj;
				LoginInApp lip = map.get(userdto.getUserName());
				if(lip!=null){	//当前用户的登录记录不为空
					
					int max_login_number = 3;//可连续登录次数
					int lock_time = 10;//持续锁定时间
					SysParam param = sysParamServiceImpl.getByParamName(Constants.SECURITY_PARAMS[0]);
					if(param!=null&&ValidateUtil.validateString(param.getParamValue())){
						max_login_number = Integer.valueOf(param.getParamValue());
					}
					SysParam param1 = sysParamServiceImpl.getByParamName(Constants.SECURITY_PARAMS[1]);					
					if(param1!=null&&ValidateUtil.validateString(param1.getParamValue())){
						lock_time = Integer.valueOf(param1.getParamValue());
					}
				
					if(lip.getLoginCount()>=max_login_number){	//超出可连续登录次数
						Calendar curCal = Calendar.getInstance();
						Calendar lockCal = new GregorianCalendar();
						lockCal.setTime(lip.getLoginTime());
						lockCal.add(Calendar.MINUTE, lock_time);
						if(curCal.before(lockCal)){
							addActionError("账号"+userdto.getUserName()+"已被锁定,请在"+lock_time+"分钟后登录!");
							return "loginerror";
						}else{//过期解锁
							lip.setLoginCount(0);
						}						
					}					
				}				
			}			
		}

  /**

	 * 修改应用程序缓存账户信息
	 * @param user
	 */
	private void modifyLoginInApp(OnlineUser user){
		
		String accountLockState = PropertiesUtil.getString(Constants.ACCOUNT_LOCK_STATE);//锁定状态开关
		if(ValidateUtil.matchString(accountLockState, "on")){	//锁定状态开启			
			
			ServletContext app = ServletActionContext.getServletContext();	//上下文		
			Map<String, LoginInApp> appInfo = (Map<String, LoginInApp>)app.getAttribute(Constants.KEY_SESSION_MESSAGE);
			
			if(ValidateUtil.matchString(user.getErrorMsg(),Constants.LOGIN_ERROR_TIP)){//登录异常				
				
				if(appInfo!=null){
					LoginInApp lip = appInfo.get(user.getUserName());
					if(lip==null){
						lip = new LoginInApp(ServletActionContext.getRequest().getSession().getId(),new Date(),1);
					}else{
						lip.setLoginTime(new Date());
						lip.setLoginCount(lip.getLoginCount()+1);
					}
					appInfo.put(user.getUserName(), lip);					
				}else{
					appInfo = new HashMap<String, LoginInApp>();
					LoginInApp lip = new LoginInApp(ServletActionContext.getRequest().getSession().getId(),new Date(),1);
					appInfo.put(user.getUserName(), lip);	
				}	
				
				app.setAttribute(Constants.KEY_SESSION_MESSAGE, appInfo);
				
			}		
			
		}
		
	}

 private void sessionManage(OnlineUser user){

		
		HttpSession session = request.getSession();				
		ServletContext context = ContextLoader.getCurrentWebApplicationContext().getServletContext();		
		Object obj = context.getAttribute(Constants.KEY_SESSION_MESSAGE);
		
		Map<String, LoginInApp> map = null;
		if(obj!=null){			
			map = (HashMap<String, LoginInApp>)obj;
		}else{
			map = new HashMap<String, LoginInApp>();
		}
		map.put(user.getUserName(),new LoginInApp(session.getId(),new Date(),0));		
		context.setAttribute(Constants.KEY_SESSION_MESSAGE,map);
	}

 

在拦截器中处理多客户端登录

			OnlineUser ouser = (OnlineUser)session.getAttribute(Constants.KEY_SESSION_ONLINE_USER);
			
			//多个客户端登录,先登录者被踢出
			ServletContext context = session.getServletContext();
			Object obj = context.getAttribute(Constants.KEY_SESSION_MESSAGE);
			if(obj!=null&&ouser!=null){			
				Map<String, LoginInApp> map = (HashMap<String, LoginInApp>)obj;
				String oldSessionId = map.get(ouser.getUserName())==null?null:map.get(ouser.getUserName()).getSessionId();
				if(ValidateUtil.validateString(oldSessionId)&&!ValidateUtil.matchString(session.getId(), oldSessionId)){
					session.removeAttribute(Constants.KEY_SESSION_ONLINE_USER);
					response.sendRedirect(request.getContextPath() + "/common/outTime.jsp");
					return;
				}
			}	
 

 

 

0
0
分享到:
评论
3 楼 hbxflihua 2013-05-13  
308745107 写道
看起来,“同一账号只会记录该账号最后一次登录的sessionId。在拦截器中对用户的会话ID进行验证,如果不一致,则为之前登录的客户端,直接将当前会话清除,以实现账号独立会话的功能。”,前面需要添加个“登录成功后,”,语言组织不力啊。

2 楼 hy806806 2012-09-29  
写的挺好,博主的字里行间处处透着谦虚,为何不自信一点,呵呵。。。
个人认为数据存至应用的Applicaion中,对于大应用会耗费内存影响性能,我们在做项目组时都是通过数据库来实现这些功能的。
 
1 楼 308745107 2012-09-29  
看起来,“同一账号只会记录该账号最后一次登录的sessionId。在拦截器中对用户的会话ID进行验证,如果不一致,则为之前登录的客户端,直接将当前会话清除,以实现账号独立会话的功能。”,前面需要添加个“登录成功后,”,语言组织不力啊。

相关推荐

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

    2. 存储到application中:将在线用户的信息保存在全局的`application`作用域内,例如Spring框架中的`ApplicationContext`。这种方法避免了频繁的数据库操作,但需要确保在用户退出时正确清理相关信息。 在本文提供...

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

    在Java Web开发中,防止多用户重复登录是一个重要的安全措施,确保每个账号只能在一个设备或浏览器会话中活跃。常见的解决方法有两种,本文主要探讨第二种方案。 首先,第一种解决方案是通过在数据库中添加一个标志...

    Web数据库技术复习[收集].pdf

    会话变量(Session)适合存储用户个人账号和权限数据。 8、正确答案:B。Request对象用于接收客户端发送的数据。 9、正确答案:C。`&lt;title&gt;`标签在HTML中可以单独出现,不需成对。 10、在WWW服务器和浏览器间主要...

    oracle语句大全

    - `sys/change_on_install`:具有最高权限的管理员账号,通常用于系统级别的管理操作。 - `system/manager`:标准的管理员账号,用于日常的数据库管理。 - `scott/tiger`:一个示例账号,用于演示和教学目的。 - `...

    登陆注册的的ap.zip

    1. **API**:API,全称为Application Programming Interface,是软件系统之间交互的接口。在这个场景下,它可能是由服务器端提供的接口,允许客户端(如网页、移动应用)发送请求,进行用户登录和注册操作。 2. **...

    DBA必须掌握的知识基础

    - 确保数据库的安全性,包括设置用户权限,定期审计,防止未授权访问,以及处理账号锁定等问题。 10. 了解数据库版本特性: - 不同版本的Oracle数据库可能存在特性差异,如文中提到的Oracle 10g和9i的闪回功能,...

    ORACLE紧急情况信息收集.docx

    - 使用`v$lock`、`v$session`等视图查找锁定会话。 - 尝试终止异常会话(`alter system kill session ',&lt;serial#&gt;'`)。 - 分析`v$process`视图中的信息了解进程状态。 **4.3 执行RDA收集信息** - **RDA(Real ...

    asp.net技术内幕(1)

    16.1.5 cookie的限制 16.2 使用会话状态 16.2.1 向会话状态中添加条目 16.2.2 从会话状态中删除条目 16.2.3 启动用户会话 16.2.4 结束用户会话 16.2.5 处理会话事件 16.2.6 在进程...

    asp.net技术内幕(2)

    16.1.5 cookie的限制 16.2 使用会话状态 16.2.1 向会话状态中添加条目 16.2.2 从会话状态中删除条目 16.2.3 启动用户会话 16.2.4 结束用户会话 16.2.5 处理会话事件 16.2.6 在进程...

    asp.net技术内幕(5)

    16.1.5 cookie的限制 16.2 使用会话状态 16.2.1 向会话状态中添加条目 16.2.2 从会话状态中删除条目 16.2.3 启动用户会话 16.2.4 结束用户会话 16.2.5 处理会话事件 16.2.6 在进程...

    asp.net技术内幕(4)

    16.1.5 cookie的限制 16.2 使用会话状态 16.2.1 向会话状态中添加条目 16.2.2 从会话状态中删除条目 16.2.3 启动用户会话 16.2.4 结束用户会话 16.2.5 处理会话事件 16.2.6 在进程...

    asp.net技术内幕(3)

    16.1.5 cookie的限制 16.2 使用会话状态 16.2.1 向会话状态中添加条目 16.2.2 从会话状态中删除条目 16.2.3 启动用户会话 16.2.4 结束用户会话 16.2.5 处理会话事件 16.2.6 在进程...

    ASP.net技术内幕

    15.2.5 处理Application_BeginRequest事件 15.3 使用Web.Config文件 15.3.1 研究配置部分 15.3.2 修改配置设置 15.3.3 设置配置位置 15.3.4 锁定配置设置 15.3.5 添加自定义的...

    Oracle应用常见傻瓜问题 1000问

    解锁操作可以通过杀死锁定会话的进程来实现,例如: ```sql ALTER SYSTEM KILL SESSION 'sid, serial#'; ``` #### 31. SQLPLUS下如何修改编辑器? 可以使用`SET EDITOR`命令来更改SQL*Plus的编辑器。例如: ```sql ...

    ORACLE常见问题解答

    这是Oracle 9i Application Server Web Cache服务的默认登录凭证,用于管理Web缓存设置和服务状态。 #### 3. Oracle 8.0.5 创建数据库 使用`orainst`工具进行图形化安装。如果系统支持Motif界面,可以通过运行`...

    25175 学生留言本 v1.0

    在ASP中,开发者可以利用内置的对象如Request、Response、Session、Application等来处理HTTP请求、发送HTTP响应、管理用户会话以及共享应用程序级的数据。 回到“学生留言本 v1.0”,这个系统的核心组件包括以下几...

    Oracle 经典 1000 问

    常见的几个账号包括: - `internal/oracle`:这个路径可能是指向某个特定的脚本或配置文件。 - `sys/change_on_install`:这是安装后默认的SYS用户的密码。 - `system/manager`:这是另一个默认的SYSTEM用户的...

Global site tag (gtag.js) - Google Analytics