分布式应用session会话管理-基于redis

session会话在单台服务器下不会出现共享问题,现在应用部署方式都是分布式,或者集群部署,这样必然会面临一个问题,session共享。

session共享的解决方案也有很多,

一、web服务器的粘性请求,比如采用nginx请求分发,使用ip_hash这种负载均衡方式,客户端请求只会被分发到相同的后台server,这样可以避免session共享的问题。但是缺点也很明显

二、基于数据库存储(网站用户量大的情况下,频繁dml数据,对db压力大)

三、基于cookie存储(安全问题、虽然可以加密存储、但是我觉得永远不能将敏感数据放在客户端,不信任啊O(∩_∩)O哈哈~)

四、服务器内置的session复制域(比如was下提供session复制功能、但这个损耗服务器内存)

五、基于nosql(memcache、redis都可以)

http请求是无状态的

这里要引入一个概念sessionid,session对象当客户端首次访问时,创建一个新的session对象.并同时生成一个sessionId,并在此次响应中将sessionId以响应报文的方式些回客户端浏览器内存或以重写url方式送回客户端,来保持整个会话

也就是说客户端request请求时候,如果获取了session,就默认分配一个jessionid,然后通过response响应到客户端cookie,然后客户端下一次请求,默认会携带这个jessionid请求到服务端,服务端拿到这个jessionid来区分不同的客户端。

说清楚这些,就可以从sessionid入手了,要实现将session信息存入redis,总结了下大概是三点:

1.实现httpsession接口,重写我们需要用到的方法,比如set get这些。。balabala一堆......

2.继承HttpServletRequestWrapper,这个类里面有getSession()方法,我们需要重写,来取自定义session

3.实现filter,用来拦截客户端请求,获取我们自定义的request,从而获得session

具体实现:

1.实现httpsession接口

public class HttpSessionWrapper implements HttpSession {
    protected final Logger logger = LoggerFactory.getLogger(getClass());
	private String sid = "";
	private HttpServletRequest request;
	private HttpServletResponse response;
	private final long creationTime = System.currentTimeMillis();
	private final long lastAccessedTime = System.currentTimeMillis();
	private SessionMeta meta;
	public HttpSessionWrapper() {
	}
    public HttpSessionWrapper(String sid,SessionMeta meta, HttpServletRequest request,
			HttpServletResponse response) {
    	this.sid=sid;
    	this.request=request;
    	this.response=response;
    	this.meta=meta;
	}
	public Object getAttribute(String name) {
    	logger.info(getClass()+"getAttribute(),name:"+name);
	  	Jedis jedis =null;
	  	Object obj =null;
	  	String jsonStr = null;
    	try {
    		jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort());
    		jsonStr = jedis.get(name);
        	if(jsonStr!=null||StringUtils.isNotEmpty(jsonStr)){
            	jedis.expire(name, meta.getSeconds());// 重置过期时间
    			obj =JSON.parseObject(jsonStr, User.class); //反序列对象
        	}
        	if (jedis != null) {
    			JedisPoolStore.getInstance().returnJedis(jedis);
    		}
        	return obj;
		}
    	catch (JSONException je) {
			logger.error(je.getMessage());
				if (null != jedis)
					JedisPoolStore.getInstance().returnJedis(jedis);
        	return jsonStr;
		}
    	catch (Exception e) {
			logger.error(e.getMessage());
			if (e instanceof JedisException) {
				if (null != jedis)
					JedisPoolStore.getInstance().returnBrokenJedis(jedis);
			} else {
				if (null != jedis)
					JedisPoolStore.getInstance().returnJedis(jedis);
			}
			throw new HttpSessionException(" session 异常  getAttribute() name:"+name);
		}
    }
    public void setAttribute(String name, Object value) {
    	logger.info(getClass()+"setAttribute(),name:"+name);
    	Jedis jedis =null;
    	try {
    		jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort());
    		if(value instanceof String){
    			String value_ =(String) value;
    			jedis.set(name,value_);//普通字符串对象
    		}else{
    			jedis.set(name, JSON.toJSONString(value));//序列化对象
    		}

        	jedis.expire(name, meta.getSeconds());// 重置过期时间
        	if (jedis != null) {
    			JedisPoolStore.getInstance().returnJedis(jedis);
    		}
		} catch (Exception e) {
			logger.error(e.getMessage());
			if (e instanceof JedisException) {
				if (null != jedis)
					JedisPoolStore.getInstance().returnBrokenJedis(jedis);
			} else {
				if (null != jedis)
					JedisPoolStore.getInstance().returnJedis(jedis);
			}
			throw new HttpSessionException(" session 异常  setAttribute() name:"+name+",value:"+value);
		}

    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public void invalidate() {
    	logger.info(getClass()+"invalidate()");
    }

    public void removeAttribute(String name) {
    	logger.info(getClass()+"removeAttribute(),name:"+name);
    	if(StringUtils.isNotEmpty(name)){
    		Jedis jedis =null;
        	try {
        		jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort());
            	jedis.del(name);
            	if (jedis != null) {
        			JedisPoolStore.getInstance().returnJedis(jedis);
        		}
    		} catch (Exception e) {
    			logger.error(e.getMessage());
    			if (e instanceof JedisException) {
    				if (null != jedis)
    					JedisPoolStore.getInstance().returnBrokenJedis(jedis);
    			} else {
    				if (null != jedis)
    					JedisPoolStore.getInstance().returnJedis(jedis);
    			}
    			throw new HttpSessionException(" session 异常  removeAttribute() name:"+name);
    		}
    	}

    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public Object getValue(String name) {
        return null;
    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public Enumeration getAttributeNames() {
    	 return  null;

    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public String[] getValueNames() {
    	 return  null;
	 }
    /**
     * 不可用
     * @deprecated
     *
     */
    public void putValue(String name, Object value) {
    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public void removeValue(String name) {
    }

    public long getCreationTime() {
    	return  creationTime;
    }

    public String getId() {
    	logger.info(getClass()+"getId():"+sid);
        return sid;
    }

    public long getLastAccessedTime() {
        return lastAccessedTime;
    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public ServletContext getServletContext() {
        return null;
    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public void setMaxInactiveInterval(int interval) {
    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public int getMaxInactiveInterval() {
        return 0;
    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public HttpSessionContext getSessionContext() {
        return null;
    }
    /**
     * 不可用
     * @deprecated
     *
     */
    public boolean isNew() {
    	logger.info(getClass()+"isNew()");
        return false;
    }
}

2.继承HttpServletRequestWrapper

/***
 *
 * @author xiaoshuai
 *
 */
public class DefinedHttpServletRequestWrapper extends HttpServletRequestWrapper{
	    protected final Logger logger = LoggerFactory.getLogger(getClass());

	    private HttpSessionWrapper currentSession;
	    private HttpServletRequest request;
	    private HttpServletResponse response;
	    private String sid = "";
		private  SessionMeta meta;

	    public DefinedHttpServletRequestWrapper(HttpServletRequest request) {
	        super(request);
	    }

	    public DefinedHttpServletRequestWrapper(String sid, HttpServletRequest request) {
	        super(request);
	        this.sid = sid;
	    }

	    public DefinedHttpServletRequestWrapper(String sid, SessionMeta meta,HttpServletRequest request,
	            HttpServletResponse response) {
	        super(request);
	        this.request = request;
	        this.response = response;
	        this.sid = sid;
	        this.meta=meta;
	    }

	    @Override
	    public HttpSession getSession(boolean create) {
	    	if(currentSession != null) {
	    	       return currentSession;
	    	     }
    	     if(!create) {
    	       return null;
    	     }
    	     currentSession = new HttpSessionWrapper(sid,meta, request, response);
    	     return currentSession;
	    }

	    @Override
	    public HttpSession getSession() {
	      return getSession(true);
	    }

}

3.实现filter

public class SessionFilter implements Filter{
	protected final Logger logger = LoggerFactory.getLogger(getClass());
	private static SessionMeta meta = new SessionMeta();
	private static final String host ="host";
	private static final String port ="port";
	private static final String seconds="seconds";

	public void init(FilterConfig filterConfig) throws ServletException {
		logger.debug("init filterConfig info");
		meta.setHost(filterConfig.getInitParameter(host));
		meta.setPort(Integer.parseInt(filterConfig.getInitParameter(port)));
		meta.setSeconds(Integer.parseInt(filterConfig.getInitParameter(seconds)));
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		 //从cookie中获取sessionId,如果此次请求没有sessionId,重写为这次请求设置一个sessionId
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
        String sid = CookieHelper.findCookieInfo(httpRequest,CookieHelper.COOKIE_DOMAIN_NAME);
        if(StringUtils.isEmpty(sid) ){
        	try {
        		sid =CookieHelper.saveCookie(SessionId.doIds(), httpResponse);
			} catch (Exception e) {
				e.printStackTrace();
			}
        }
        logger.info("JESSIONID:"+sid);
        chain.doFilter(new DefinedHttpServletRequestWrapper(sid,meta,httpRequest, httpResponse), response);
	}

	public void destroy() {
	}

}

3.配置web.xml

	<!-- session过滤器 -->
	<filter>
		<filter-name>sessionFilter</filter-name>
		<filter-class>cn.session.filter.SessionFilter</filter-class>
		<init-param>
			<param-name>host</param-name>
			<param-value>10.1.193.1</param-value>
		</init-param>
		<init-param>
			<param-name>port</param-name>
			<param-value>6372</param-value>
		</init-param>
		<init-param>
			<param-name>seconds</param-name>
			<param-value>1800</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>sessionFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

第一次产生sessionid访问:

系统登录后存用户信息至redis:

关于安全这块,因为不管登录系统与否,sessionid都会产生,这时候就会产生一个问题,因为cookie是可以被修改的,就会产生一个问题,撞session的分享。。。换成不同的sessionid去请求系统。。。总有一天会撞上。。

SO,我这边是这样处理的,

当登录成功之后,产生一个token令牌,产生规则的话自己定,一堆密钥比如系统名字+sessionid+userid+固定字符,产生一个加密的字符串,放入cookie。

这样当我们获取当前登录用户时,解密token,获取sessionid,然后取redis用户信息。。(切记这里不要直接通过sessionid取用户信息,有风险!!!)

用户没有登录成功,自然也没有这个令牌。。。

这里又有另外一个问题,用户如果禁用cookie呢????? so what.....   下次再说这个。。。

水平有限,如果你有更好的方式,请联系我,交流................................tks!!!!

时间: 2024-07-31 22:35:54

分布式应用session会话管理-基于redis的相关文章

把php session 会话保存到redis

php的session会话默认时以文件形式保存在php.ini配置文件设置的会话缓存目录,文件保存会话的效率很低,每当每个用户登录一次就会在服务器上生成一个唯一的session_id文件,当用户登录量达到几十万级别的时候,就会有几十万个文件在服务器生成,这时候磁盘的会话目录的IO读写会非常的慢,导致用户登录时程序会花费大量的时间在会话文件的读写上.当大量的用户登录时读写能力不够,导致的文件是会被第一个操作session的进程锁定,其他请求阻塞.请求会挂起在session_start()直到ses

深入理解JSP/Servlet Session会话管理机制

HTTP 是一种无状态协议,这意味着每次客户端检索网页时,都要单独打开一个服务器连接,因此服务器不会记录下先前客户端请求的任何信息.它与FTP.Telnet等协议不同,FTP等协议可以记住用户的连接信息. 会话(Session)是指一个终端用户与交互系统进行通信的时间间隔,通常指从登陆系统到注销系统之间所经过的时间以及如果需要的话,可能还有一定操作空间.JSP有四种方式实现会话跟踪功能. Cookie 服务器在响应请求时可以将一些数据以"键-值"对的形式通过响应信息保存在客户端.当浏览

第二章 Session会话管理

采用网址重写的缺点: 在有些Web浏览器中,URL限制为2000个字符. 仅当有链接要插入值时,值才能转换成后面的资源.此外,要把值添加到静态页面的链接中,可不是一件容易的事情. 网址重写必须在服务器端有效.所有的链接都必须带有值,这样可能出现一个问题,即一个页面中可能会有许多个链接. 某些字符,例如空格.&符号及问号都必须进行编码. 添加到URL中的信息是明显可见的,这种情况有时可不是我们所期待的. 网址重写案例设计: 1.访问top10,返回一个页面,该页面中的每个按钮的url都被重写了,加

图文并茂超详细搭建redis缓存服务器(nginx+tomcat+redis+mysql实现session会话共享)

博主QQ:819594300 博客地址:http://zpf666.blog.51cto.com/ 有什么疑问的朋友可以联系博主,博主会帮你们解答,谢谢支持! 一.redis介绍 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set --有序集合)和hash(哈希类型).与memcached一样,为了保证效率,数据都是缓存在内存中.区别的是redis会

session management会话管理的原理

web请求与响应基于http,而http是无状态协议.所以我们为了跨越多个请求保留用户的状态,需要利用某种工具帮助我们记录与识别每一次请求及请求的其他信息.举个栗子,我们在淘宝购物的时候,首先添加了一本<C++ primer>进入购物车,然后我们又继续去搜索<thinking in java>,继续添加购物车,这时购物车应该有两本书.但如果我们不采取session management会话管理的话,基于http无状态协议,我们在第二次向购物车发出添加请求时,他是无法知道我们第一次添

4.会话管理(Session)

1.会话管理的概念和基本原理: 会话管理概念: 会话的实现过程: 2.使用Cookie.隐藏域.URL重写实现会话管理 创建并向客户端发送Cookie; 从客户端读取Cookies Cookie的方法: Cookie的优缺点: 使用隐藏的表单域: 使用URL重写: Session会话管理的原理和技术实现: 使用Session对象: 会话失效: Session 与URL重写 Session的方法: 原文地址:https://www.cnblogs.com/Firesun/p/9655479.htm

redis缓存服务器(Nginx+Tomcat+redis+MySQL实现session会话共享)

一.redis介绍 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set --有序集合)和hash(哈希类型).与memcached一样,为了保证效率,数据都是缓存在内存中.区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现master-slave(主从)同步. Redis是一个高性能的key-valu

使用Spring Session做分布式会话管理

在Web项目开发中,会话管理是一个很重要的部分,用于存储与用户相关的数据.通常是由符合session规范的容器来负责存储管理,也就是一旦容器关闭,重启会导致会话失效.因此打造一个高可用性的系统,必须将session管理从容器中独立出来.而这实现方案有很多种,下面简单介绍下: 第一种是使用容器扩展来实现,大家比较容易接受的是通过容器插件来实现,比如基于Tomcat的tomcat-redis-session-manager,基于Jetty的jetty-session-redis等等.好处是对项目来说

基于redis的处理session的方法

一个基于redis的处理session的方法,如下. 1 <?php 2 class Session_custom { 3 private $redis; // redis实例 4 private $prefix = 'sess_'; // session_id前缀 5 6 // 会话开始时,会执行该方法,连接redis服务器 7 public function open($path, $name) { 8 $this->redis = new Redis(); 9 return $this-