1 session存储策略html
存储,即在后台使用session的setAttribute,getAttribute等方法时,这些内部存放的数据最终存储至什么位置。好比在默认的tomcat实现中,相应的数据即存储在内存中,并在中止以后会序列化至磁盘中。
可使用内存存储和第三方存储,如redis。对于集群式的session存储,那么确定会使用第三方存储的实现。html5
在spring-session中,对session存储单独做了一个定义,但定义上基本保证与http session一致,主要的目的在于它能够支持在非http的环境中模拟使用。所以不直接使用http session接口。
先看定义:web
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接口。redis
1.0 id主键的生成spring
对于id,即理解为session实体对象唯一键,能够采用任意的一种唯一key生成策略。好比,使用uuid来生成唯一键。同时,也能够将这个id认为就是在http中sessionCookie的值后端
1.1 map存储浏览器
若是是map来存储,那么 set,get,remove就能够直接使用map的相应实现便可。在spring-session实现中MapSession,即采用内部的一个hashmap来进行存储。tomcat
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。以下的定义所 示:
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响应被正确地处理
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的处理即达到相应的处理透明便可。