分布式cookie-session的实现(spring-session)

本文使用的spring-session版本为 1.0.0,地址为: https://github.com/spring-projects/spring-session

1     session存储策略

存储,即在后台使用session的setAttribute,getAttribute等方法时,这些内部存放的数据最终存储至什么位置。比如在默认的tomcat实现中,相应的数据即存储在内存中,并在停止之后会序列化至磁盘中。
可以使用内存存储和第三方存储,如redis。对于集群式的session存储,那么肯定会使用第三方存储的实现。

在spring-session中,对session存储单独作了一个定义,但定义上基本保证与http session一致,主要的目的在于它可以支持在非http的环境中模拟使用。因此不直接使用http session接口。
先看定义:

1
2
3
4
5
6
7
8
public interface Session {
     /** 获取惟一的id,可以理解为即将每个session当作一个实体对象 */
    String getId();
   <T> T getAttribute(String attributeName);
    Set<String> getAttributeNames();
    void setAttribute(String attributeName, Object attributeValue);
    void removeAttribute(String attributeName);
}

从这个定义来看,如果要使用httpSession,如果实现了这个session接口,那么实现上就可以全部实现httpSession对于存储的要求。对于非httpSession场景,使用这个也可以达到session存储的目的。
当然,为了保证在会话场景中会使用到失效,最后访问时间,最大不活跃时间的目的,spring-session也有一个继承于session接口的expiringSession接口。

1.0    id主键的生成

对于id,即理解为session实体对象惟一键,可以采用任意的一种惟一key生成策略。比如,使用uuid来生成惟一键。同时,也可以将这个id认为就是在http中sessionCookie的值

1.1     map存储

如果是map来存储,那么 set,get,remove就可以直接使用map的相应实现即可。在spring-session实现中MapSession,即采用内部的一个hashmap来进行存储。

1.2     redis存储

在sprng-session中,将属性的存储和整体的存储分开,使用专门的仓库来处理session实体的处理。对于从领域模型的概念来说,set,get,removeAttribute只是对属性的处理,处理的是一个内部状态的变化,对于整体的实体来说,并没有整体上的处理。具体到实现,在redis的存储实现中,spring-session内部使用了一个 mapSession来存储相应的属性信息。
在一个实体被创建之后,相应的属性信息被全部设置至mapSession中,然后接下来的属性访问均通过mapSession来处理。为了最终存储相应的数据值,redis实现使用了一个额外的deltaMap来存放变化了的数据,并在重新保存时,一次性地将所有属性put至redis中。
从这个实现来看,spring-session的实现并不是实时地与redis进行交互,这种实现方式在解决同一session设置冲突属性时可能存在问题。因此存在这种场景的,需要重新实现相应的逻辑。

2     session仓库策略

之所以将存储策略和仓库策略分开,也是spring-session的一个设计思想。spring将session设计为一个实体,因此将实体对象的操作和属性的操作进行拆分。在前面的存储策略已经描述了属性的操作,因此这里的仓库策略主要处理实体的创建,获取,更新,删除等,即CRUD。如下的定义所示:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public interface SessionRepository<S extends Session> {
 
     /** 新建 */
    S createSession();
 
     /** 保存或更新 */
    void save(S session);
 
     /** 获取 */
    S getSession(String id);
 
     /** 删除 */
    void delete(String id);
}

2.1     本地仓库
如果是本地仓库,那么可以使用一个本地全局的globalMap,然后使用sessionId作为key,mapSession作为value来实现即可。

2.2     redis仓库

如果是resi仓库,那么在新建时,只是在本地创建一个redisSession对象,然后在redis存储中使用一个 hash结构来进行存储。即<I,<K,V>>的结构。其中I即表示sessionId,<k,V>表示具体的redisSession对象。其中getSession,delete都是针对I的操作,而save则在在指定I的情况下,直接保持<K,V>即可。

为了在一定程度上进行优化,以及解决在一定程序上的覆盖问题,在redis实现时。spring-session只对修改过的属性作存储,即当使用setAttribute处理属性时,spring-session使用额外的delta来保存修改过的属性,最后进行save时,只操作这部分数据即可。并且redis也支持调用hSet来保存或更新修改过的数据。

3     session传输策略

3.1     cookie传输

常规的实现也是使用cookie传输,比如tomcat,jetty等。在这种情况下,后端在创建session时,如果这是新的session,会自动调用原生response.addCookie以创建一个新的cookie信息。为保证安全,采用httpOnly进行创建。在发送给客户端之后的每次交互,浏览器会自动将这些信息传输至服务器端。然后服务器端直接从cookie中获取到sessionId,再进一步操作即可。
对于这种实现,应用程序开发不需要作任何的调整,浏览器会自动处理cookie的传输。

3.2     header传输

header传输,即在创建新session时,往response header中使用addHeader添加一个新的响应头,如session头,headerValue中存放相应的sessionId值。
在客户端实现中,客户端代码需要手动地将响应值进行记录,并在请求时加入到请求头当中。并且需要监听响应头值的变化,比如sessionId值的变化处理。相比cookie传输来说,这种实现对于开发更复杂一点。

4     http协议兼容

4.1     为保证http协议兼容,在session信息准备好之后,其必须在响应头之后进行处理

不管是使用cookie传输还是header传输,在响应时都需要保证在响应body输出时,相应的头信息都应该先被处理。(cookie也是一种特殊的header)。如果像以下的代码,就不能保证session响应被正确地处理

1
2
3
4
X implements Filter {
     filterChain.doFilter(request,response)
     // do session logic
}

在这种实现中,如果在应用代码中直接使用response的outputStream进行输出时,这里的过滤器实现中的do Session操作就不能实现相应的逻辑。因为http协议中的消息机制已经不再允许再输出body之后输出header信息了。

在spring-session的实现中,我们只需要保证在调用response处理响应body时,能够处理session逻辑即可。那么可以使用一种hack的技术,针对在response输出时,判定是否在处理body或应该结束响应时,加入特定的hack以处理我们的do session操作。因此提供了 OnCommittedResponseWrapper 以便在特定的时机进行处理,在具体的 onResponseCommitted中,处理session的delete响应和save等即可。

4.2     为支持多浏览器访问,因此在特定的路径信息处理时,应当自动地处理相应的browser参数

关于多browser支持,可以先看看此篇文章。
http://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-multi

在多browser支持时,主要即在请求参数中增加相应的浏览器识别参数_s,那么在需要进行界面跳转或其它地址处理时,后台应该能够自动地处理相应的参数信息。在spring-session中,为了对应用程序透明,通过重写response的encodeURL和encodeRedirectURL时,增加对相应_s的处理即达到相应的处理透明即可。

转载请标明出处:i flym
本文地址:http://www.iflym.com/index.php/code/201503170001.html

分布式cookie-session的实现(spring-session)的更多相关文章

  1. Spring Session实现分布式session的简单示例

    前面有用 tomcat-redis-session-manager来实现分布式session管理,但是它有一定的局限性,主要是跟tomcat绑定太紧了,这里改成用Spring Session来管理分布 ...

  2. 使用Spring Session实现Spring Boot水平扩展

    小编说:本文使用Spring Session实现了Spring Boot水平扩展,每个Spring Boot应用与其他水平扩展的Spring Boot一样,都能处理用户请求.如果宕机,Nginx会将请 ...

  3. Re:从零开始的Spring Session(一)

    Session和Cookie这两个概念,在学习java web开发之初,大多数人就已经接触过了.最近在研究跨域单点登录的实现时,发现对于Session和Cookie的了解,并不是很深入,所以打算写两篇 ...

  4. 通过Spring Session实现新一代的Session管理

    长期以来,session管理就是企业级Java中的一部分,以致于我们潜意识就认为它是已经解决的问题,在最近的记忆中,我们没有看到这个领域有很大的革新. 但是,现代的趋势是微服务以及可水平扩展的原生云应 ...

  5. 转:通过Spring Session实现新一代的Session管理

    长期以来,session管理就是企业级Java中的一部分,以致于我们潜意识就认为它是已经解决的问题,在最近的记忆中,我们没有看到这个领域有很大的革新. 但是,现代的趋势是微服务以及可水平扩展的原生云应 ...

  6. 大话 Spring Session 共享

    javaweb中我们项目稍微正规点,都会用到单点登录这个技术.实现它的方法各家有各界的看法.这几天由于公司项目需求正在研究.下面整理一下最近整理的心得. 简介 在分布式项目中另我们头疼的是多项目之间的 ...

  7. Spring Session工作原理

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/KCOFv0nRuymkX79-RZi9eg 作者:张正林 目录:1.引入背景2.使用方法3.工作流程 ...

  8. 通过 Spring Session 实现新一代的 Session 管理

    长期以来,session 管理就是企业级 Java 中的一部分,以致于我们潜意识就认为它是已经解决的问题,在最近的记忆中,我们没有看到这个领域有很大的革新. 但是,现代的趋势是微服务以及可水平扩展的原 ...

  9. 实战开发,使用 Spring Session 与 Spring security 完成网站登录改造!!

    上次小黑在文章中介绍了四种分布式一致性 Session 的实现方式,在这四种中最常用的就是后端集中存储方案,这样即使 web 应用重启或者扩容,Session 都没有丢失的风险. 今天我们就使用这种方 ...

  10. spring session 和 spring security整合

    背景: 我要做的系统前面放置zuul. 使用自己公司提供的单点登录服务.后面的业务应用也是spring boot支撑的rest服务. 目标: 使用spring security管理权限包括权限.用户请 ...

随机推荐

  1. mysql 查询 字段的类型

    select column_name,data_type from information_schema.columnswhere table_name = '表名'

  2. 1058FBI<二叉树,递归,后序遍历>

    问题描述 我们可以把由"0"和"1"组成的字符串分为三类:全"0"串称为B串,全"1"串称为I串,既含"0&q ...

  3. 开发板S3C2440挂起NFS步骤

    第一.安装.配置.启动FTP.SSH或NFS服务 参考韦东山的嵌入式linux应用开发完全手册 http://pan.baidu.com/s/1o79h3n0 第二.windows.linux以及开发 ...

  4. HDU 4287 Intelligent IME(字典树)

    在我没用hash之前,一直TLE,字符串处理时间过长,用了hash之后一直CE,(请看下图)我自从经历我的字典树G++MLE,C++AC以后,一直天真的用C++,后来的CE就是因为这个,G++才支持这 ...

  5. HDU 1527 取石子游戏(威佐夫博弈)

    基础威佐夫博弈,判断奇异局势即可,判断方式为k为两数之差绝对值,(sqrt(5) + 1) / 2 * k若等于两数小者则为奇异局势,也就是必败态. #include<stdio.h> # ...

  6. java中创建多线程的方式

    在java中比较常用的有三种创建多线程的方式. 方式一:继承Thread类,要重写run方法. 在MyThread类 public class MyThread extends Thread { @O ...

  7. 基于POI的Excel导入导出(JAVA实现)

    今天做了个excel的导入导出功能,在这记录下. 首先现在相关poi的相关jar包,资源链接:http://download.csdn.net/detail/opening_world/9663247 ...

  8. ScreenCaptureHtmlUnitDriver.java

    https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-server/src/test/java/com/webautoma ...

  9. nodejs连接mysql实例

    1.在工程目录下运行npm install mysql安装用于nodejs的mysql模块: 2.创建db.js模块用于连接mysql,同时定义query查询方法: var mysql = requi ...

  10. ZenCoding 个人理解和总结

    我的理解:ZenCoding是一个html简写的语法,可以最快速的生成html. 不少IDE应该都支持,我用的intellij idea是支持的. ZenCoding表示和CSS/JS有相通之处,比如 ...