文档章节

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

猪刚烈
 猪刚烈
发布于 2014/09/24 13:55
字数 1040
阅读 97
收藏 3

       在上一篇文章中,介绍了使用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比对,会损失一部分性能。



本文转载自:http://blog.csdn.net/jrn1012/article/details/25790421

猪刚烈
粉丝 22
博文 708
码字总数 110
作品 1
海淀
程序员
私信 提问
spring-mvc加spring security 的简单应用

先发几张图片看下大致的样子,美工不专业,有点渣,大家找自己想了解的就好。 实现功能: 1、当没有登录的时候访问数据库有的资源,直接跳转到登录页面 2、用户登录后,在url栏直接访问没有权...

我要五个字
2014/11/14
449
0
Archx/spring-agg

#SPRING-AGG 这个一个框架整合案列,包含 SpringMVC/MyBatis/Apache Shiro 。 演示数据 演示数据请导入 db.sql 演示账号密码均为 SpringMVC 配置 演示项目使用的是 RESTful 风格,不是传统的...

Archx
2015/01/12
0
0
spring boot框架学习8-【干货】spring boot的web开发(4)-自定义拦截器处理权限

本章节主要内容: 通过前面的学习,我们了解并快速完成了spring boot第一个应用。spring boot企业级框架,那么spring boot怎么读取静态资源?如js文件夹,css文件以及png/jpg图片呢?怎么自定...

中凯_凯哥java
2017/11/11
57
0
疯狂Spring Cloud连载(9)——RestTemplate的负载均衡原理

本文节选自《疯狂Spring Cloud微服务架构实战》 京东购买地址:https://item.jd.com/12256011.html 当当网购买地址:http://product.dangdang.com/25201393.html Spring Cloud教学视频:htt...

杨大仙的程序空间
2017/10/18
859
2
SpringMVC拦截器(实现登录验证拦截器)

SpringMVC拦截器(实现登录验证拦截器) lenglingx的个人页面2017-11-281 阅读 springmvc登录验证 本例实现登陆时的验证拦截,采用SpringMVC拦截器来实现 核心代码 首先是index.jsp,显示链接...

lenglingx的个人页面
2017/11/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

JS基础-该如何理解原型、原型链?

JS的原型、原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯定不能对这个...

OBKoro1
今天
7
0
高防CDN的出现是为了解决网站的哪些问题?

高防CDN是为了更好的服务网络而出现的,是通过高防DNS来实现的。高防CDN是通过智能化的系统判断来路,再反馈给用户,可以减轻用户使用过程的复杂程度。通过智能DNS解析,能让网站访问者连接到...

云漫网络Ruan
今天
14
0
OSChina 周一乱弹 —— 熟悉的味道,难道这就是恋爱的感觉

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :好久没分享歌了分享张碧晨的单曲《今后我与自己流浪》 《今后我与自己流浪》- 张碧晨 手机党少年们想听歌,请使劲儿戳(这里)...

小小编辑
今天
3K
24
SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
46
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部