当我们一个客户端请求服务时,如果是第一次请求该服务,服务容器(tomcat,jetty,jboss)等会创建一个会话session并且把sessionid通过响应头下发给客户端,客户端后续在访问这个域下的任何服务,会通过请求头cookie把sessionID提交到服务容器,服务容器由此来判断是否是同一会话
以上是我们原始的session原理,在大部分情况下原始的session是没有问题的。
如果现在有一种场景 我们需要在同一浏览器种多账号同时在线,这种场景传统模式的session就不太适用此业务场景
这时候一种新型的session架构模式就登场了-spring-session
先说下spring-session支持功能
1.轻易把session存储到第三方存储容器,框架提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多种存储session的容器的方式。
2.同一个浏览器同一个网站,支持多个session问题。
3.Restful API,不依赖于cookie。可通过header来传递jessionID
4.WebSocket和spring-session结合,同步生命周期管理
spring-session 框架内部分析
spring-session 几个核心接口如下:
Session:这个接口定义了session基本功能
public interface Session {
String getId(); //获取sessionId
Set
void setAttribute(String attributeName, Object attributeValue); //设置属性
void removeAttribute(String attributeName); //删除属性
}
SessionRepository 用来增删改查 Session 在对应数据源中的接口
public interface SessionRepository
S createSession(); //创建session
void save(S session); //更新session
S getSession(String id);//根据 ID 来获取 Session
void delete(String id);//根据 ID 来删除 Session
}
Spring Session 认为将请求与特定的 session 实例关联起来的问题是与协议相关的,因为在请求 / 响应周期中,客户端和服务器之间需要协商同意一种传递 session id 的方式。例如,如果请求是通过 HTTP 传递进来的,那么 session 可以通过 HTTP cookie 或 HTTP Header 信息与请求进行关联。如果使用 HTTPS 的话,那么可以借助 SSL session id 实现请求与 session 的关联。如果使用 JMS 的话,那么 JMS 的 Header 信息能够用来存储请求和响应之间的 session id。
对于 HTTP 协议来说,Spring Session 定义了HttpSessionStrategy接口以及两个默认实现,即CookieHttpSessionStrategy和HeaderHttpSessionStrategy,其中前者使用 HTTP cookie 将请求与 session id 关联,而后者使用 HTTP header 将请求与 session 关联
Spring Session 对 HTTP 的支持
Spring Session 对 HTTP 的支持是通过标准的 servlet filter(SessionRepositoryFilter) 来实现的,这个 filter 必须要配置为拦截所有的 web 应用请求,并且它应该是 filter 链中的第一个 filter。Spring Session filter 会确保随后调用javax.servlet.http.HttpServletRequest的getSession()方法时,都会返回 Spring Session 的HttpSession实例,而不是应用服务器默认的 HttpSession
每个浏览器多个 Session
Spring Session 会为每个用户保留多个 session,这是通过使用名为“_s”的 session 别名参数实现的。例如,如果到达的请求为 http://example.com/doSomething?_s=0 ,那么 Spring Session 将会读取“_s”参数的值,并通过它确定这个请求所使用的是默认 session。
如果到达的请求是 http://example.com/doSomething?_s=1的话,那么 Spring Session 就能知道这个请求所要使用的 session 别名为 1. 如果请求没有指定“_s”参数的话,例如 http://example.com/doSomething,那么 Spring Session 将其视为使用默认的 session,也就是说_s=0。
要为某个浏览器创建新的 session,只需要调用javax.servlet.http.HttpServletRequest.getSession()就可以了,就像我们通常所做的那样,Spring Session 将会返回正确的 session 或者按照标准 Servlet 规范的语义创建一个新的 session。下面的表格描述了针对同一个浏览器窗口,getSession()面对不同 url 时的行为
如上面的表格所示,session 别名不一定必须是整型,它只需要区别于其他分配给用户的 session 别名就可以了。但是,整型的 session 别名可能是最易于使用的,Spring Session 提供了HttpSessionManager接口,这个接口包含了一些使用 session 别名的工具方法。
我们可以在HttpServletRequest中,通过名为“org.springframework.session.web.http.HttpSessionManager”的属性获取当前的HttpSessionManager。如下的样例代码阐述了如何得到 HttpSessionManager,并且在样例注释中描述了其关键方法的行为
以上大致介绍了下spring-session的整体架构以及核心接口和过滤器,下边我介绍下如果实现单浏览器多session
首先在启动类中加入@EnableSpringHttpSession 注解告诉应用我采用spring-session来管理容器的session
配置一个cookieConfig
注入spring-session依赖的存储容器,我这里用的是内存存储Map结构模型存储,还可以用redis,mongodb等(目前最常用的是redis)
最后自定义一个过滤器让他处于过滤器的最顶端保证此过滤器在SessionRepositoryFilter前执行原因是因为在这个自定义过滤器中要通过一定的业务规则告诉spring-session要选用哪个session比如说session容器中现在用_s0,_s1 要通过具体的业务规则选用session的索引我这里是通过我们业务上的租户来选用具体可以参考如下:
选用完具体的session以后,把请求过来的url加入_s参数,此参数就表示要告诉SessionRepositoryFilter 来选用哪个session如果有就用,没有就新创建session
至此一个相对完整单浏览器多session方案就形成了