`
hbxflihua
  • 浏览: 661027 次
  • 性别: 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进行验证,如果不一致,则为之前登录的客户端,直接将当前会话清除,以实现账号独立会话的功能。”,前面需要添加个“登录成功后,”,语言组织不力啊。

相关推荐

Global site tag (gtag.js) - Google Analytics