负载均衡下,数据库+Spring MVC拦截器禁止用户重复登录

       在上一篇文章中,介绍了使用session监听+spring MVC拦截器禁止用户重复登录 。但随着用户数量的增大,需要采用多服务器构建负载均衡,以分担大量用户访问对系统造成的压力。此时为了禁止用户重复登录,使用session监听+Spring MVC拦截器的方式存在一定问题,因为用户登录后路由到哪台服务器具有不确定性,比如用户第一次登录后被路由到服务器A,不能保证用户1小时后重复登录后被路由到服务器A。因此通过服务器缓存的sesson集合判断用户是否已经登录过不可行。

        而通过数据库+Spring MVC拦截器的的基本思路是:建立一张用户在线表(UserOnLine),用户每次登录时,记录当前sessionID,或者用户第几次登录version(本文记录version)。在Spring MVC拦截器中校验数据库中version是否和当前session中的version相等,不相等强制session过期,并提示用户重复登录,强制退出到登录界面。

           具体如下:

1、UserOnLine表实体(项目中使用hibernate注解方式)

package com.cnpc.base.user.model;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name = "USER_ONLINE")
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler","fieldHandler" })
public class UserOnLine implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5030026318119472029L;

	@Id
	@Column(name = "id", length = 36)
	@GeneratedValue(generator = "uuid")
	@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
	@JsonProperty("id")
	private String id;

	/** 用户ID**/
	@Column(name = "userid", length = 36)
	private String userid;

	/** 登录时间 **/
	@Column(name = "loginTime")
	private Date loginTime;

	/**
	 * 用户登录次数
	 */
	@Column(name = "version")
	private int version;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getUserid() {
		return userid;
	}

	public void setUserid(String userid) {
		this.userid = userid;
	}

	public Date getLoginTime() {
		return loginTime;
	}

	public void setLoginTime(Date loginTime) {
		this.loginTime = loginTime;
	}

	public int getVersion() {
		return version;
	}

	public void setVersion(int version) {
		this.version = version;
	} 
	
}

2、用户成功登录后,更新user_online表

public void sessionHandlerByTable(HttpSession session){
		String userid=session.getAttribute("userid").toString();
		UserOnLine user=userService.getUserOnLineByUserId(userid);
		if(user==null){
			user=new UserOnLine();
			user.setLoginTime(new Date());
			user.setUserid(userid);
			user.setVersion(1);
			user=(UserOnLine)userService.save(user);
		}
		else{
			user.setLoginTime(new Date());
			user.setVersion(user.getVersion()+1);
			user=(UserOnLine)userService.update(user);
		}
		session.setAttribute("version", user.getVersion());//session中保存用户登录次数	
	}

3、Spring MVC拦截器authIntercepter(拦截器的配置见上篇文章)

package com.cnpc.framework.interceptor;

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

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.cnpc.base.user.service.UserService;
import com.cnpc.framework.common.SessionContainer;

@Component("SpringMVCInterceptor")
public class AuthInterceptor extends HandlerInterceptorAdapter {    
    @Resource 
    private UserService userService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8"); 
        response.setContentType("text/html;charset=UTF-8");
 
        // 后台session控制
        String[] noFilters = new String[] { "/auth/login", "/auth/logout" };
        String uri = request.getRequestURI();

        boolean beFilter = true;
        for (String s : noFilters) {
            if (uri.indexOf(s) != -1) {
                beFilter = false;
                break;
            }
        }
        SessionContainer sessionContainer = (SessionContainer) request.getSession().getAttribute("SessionContainer");
        if (beFilter) {
            if (null == sessionContainer) {
                if (request.getHeader("x-requested-with") != null
                        && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))// 如果是ajax请求响应头会有,x-requested-with;
                {
                    response.setHeader("sessionstatus", "timeout");// 在响应头设置session状态
                    return false;
                }
                // 未登录
                PrintWriter out = response.getWriter();
                StringBuilder builder = new StringBuilder();
                builder.append("<script type=\"text/javascript\" charset=\"UTF-8\">");
                builder.append("alert(\"页面过期,请重新登录\");");
                builder.append("window.top.location.href='/auth/logout';");
                builder.append("</script>");
                out.print(builder.toString());
                out.close();
                return false;
            } else { 
                int version=userService.getUserOnLineByUserId(request.getSession().getAttribute("userid").toString()).getVersion();
                if(version!=Integer.parseInt(request.getSession().getAttribute("version").toString())){
                    //强制session超时
                    request.getSession().invalidate();    
                    if (request.getHeader("x-requested-with") != null
                            && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))// 如果是ajax请求响应头会有,x-requested-with;
                    {
                        response.setHeader("sessionstatus", "repeatlogin");// 在响应头设置session状态
                        return false;
                    }   
                    PrintWriter out = response.getWriter();
                    StringBuilder builder = new StringBuilder();
                    builder.append("<script type=\"text/javascript\" charset=\"UTF-8\">");
                    builder.append("alert(\"您的帐号已在其他机器登录,请重新登录\");");
                    builder.append("window.top.location.href='/auth/logout';");
                    builder.append("</script>");
                    out.print(builder.toString());
                    out.close();
                    return false;
                }    
                // 添加系统日志
                // -----------------------------------
                // -----------------------------------
            }
        }
        Map paramsMap = request.getParameterMap();
        return super.preHandle(request, response, handler);
    }
}

在客户端以ajax方式同服务器交互时,客户端还要处理session过期后的跳转,其代码如下(放入公共js文件中)

 $.ajaxSetup({   
       contentType:"application/x-www-form-urlencoded;charset=utf-8",   
	   complete:function(XMLHttpRequest,textStatus){   
	         var sessionstatus=XMLHttpRequest.getResponseHeader("sessionstatus"); // 通过XMLHttpRequest取得响应头,sessionstatus,
	           if(sessionstatus=="timeout"){   
	                 // 如果超时就处理 ,指定要跳转的页面	
	        	 alert("页面过期,请重新登录"); 
	                 window.top.location.href="/auth/logout";
	                }  
	           if(sessionstatus=="repeatlogin"){   
	                 alert("您的帐号已在其他机器登录,请重新登录");  
	                 window.top.location.href="/auth/logout";   
	                 }
	              }     
	           } 
	      ); 

以上方式实现了负载均衡下,禁止用户重复登录的功能。当然也可用在单机服务器上。唯一的不足是每次要从数据库中取出该用户的version同session中的version比对,会损失一部分性能。



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值