2019独角兽企业重金招聘Python工程师标准>>>

spring-session源码解读 sesion 博客分类: java spring

摘要: session通用策略 Session在浏览器通常是通过cookie保存的,cookie里保存了jessionid,代表用户的session id。一个访问路径只有一个session cookie(事实上在客户端就只有一个cookie,jsessionid是作为cookie值的一部分,这里把cookie抽象成类似服务器端的实现),也就是一个访问路径在一个浏览器上只有一个se

session通用策略

Session在浏览器通常是通过cookie保存的,cookie里保存了jessionid,代表用户的session id。一个访问路径只有一个session cookie(事实上在客户端就只有一个cookie,jsessionid是作为cookie值的一部分,这里把cookie抽象成类似服务器端的实现),也就是一个访问路径在一个浏览器上只有一个session,这是绝大多数容器对session的实现。而spring却可以支持单浏览器多用户session。下面就看看spring是怎样去支持多用户session的。

对多用户session的支持

spring session通过增加session alias概念来实现多用户session,每一个用户都映射成一个session alias。当有多个session时,spring会生成“alias1 sessionid1 alias2 sessid2…….”这样的cookie值结构。

spring session提交时如果有新session生成,会触发onNewSession动作生成新的session cookie

public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {Set<String> sessionIdsWritten = getSessionIdsWritten(request);if(sessionIdsWritten.contains(session.getId())) {return;}sessionIdsWritten.add(session.getId());Map<String,String> sessionIds = getSessionIds(request);String sessionAlias = getCurrentSessionAlias(request);sessionIds.put(sessionAlias, session.getId());Cookie sessionCookie = createSessionCookie(request, sessionIds);response.addCookie(sessionCookie);}

a) 确保已经存在cookie里的session不会再被处理。 
b) 生成一个包含所有alias的session id的map,并通过这个map构造新的session cookie值。

createSessionCookie会根据一个alias-sessionid的map去构造session cookie。

private Cookie createSessionCookie(HttpServletRequest request,Map<String, String> sessionIds) {//cookieName是"SESSION",spring的session cookie都是//以"SESSION"命名的Cookie sessionCookie = new Cookie(cookieName,"");//省略部分非关键逻辑if(sessionIds.isEmpty()) {sessionCookie.setMaxAge(0);return sessionCookie;}if(sessionIds.size() == 1) {String cookieValue = sessionIds.values().iterator().next();sessionCookie.setValue(cookieValue);return sessionCookie;}StringBuffer buffer = new StringBuffer();for(Map.Entry<String,String> entry : sessionIds.entrySet()) {String alias = entry.getKey();String id = entry.getValue();buffer.append(alias);buffer.append(" ");buffer.append(id);buffer.append(" ");}buffer.deleteCharAt(buffer.length()-1);sessionCookie.setValue(buffer.toString());return sessionCookie;}

a) 当session被invalidate,可能会存在seesionids为空的情况,这种情况下将session cookie的最大失效时间设成立即。 
b) 如果只有一个session id,则和普通session cookie一样处理,cookie值就是session id。 
c) 如果存在多个session id,则生成前文提到的session cookie值结构。

session cookie的获取

getSessionIds方法会取出request里的session cookie值,并且对每种可能的值结构进行相应的格式化生成一个key-value的map。

public Map<String,String> getSessionIds(HttpServletRequest request) {Cookie session = getCookie(request, cookieName);String sessionCookieValue = session == null ? "" : session.getValue();Map<String,String> result = new LinkedHashMap<String,String>();StringTokenizer tokens = new StringTokenizer(sessionCookieValue, " ");//单用户cookie的情况if(tokens.countTokens() == 1) {result.put(DEFAULT_ALIAS, tokens.nextToken());return result;}while(tokens.hasMoreTokens()) {String alias = tokens.nextToken();if(!tokens.hasMoreTokens()) {break;}String id = tokens.nextToken();result.put(alias, id);}return result;}
  1. 对单用户session cookie的处理,只取出值,默认为是默认别名(默认为0)用户的session。
  2. 对多用户,则依据值结构的格式生成alias-sessionid的map。
  3. 以上两种格式化都是对创建session的逆操作。

getCurrentSessionAlias用来获取当前操作用户。可以通过在request里附加alias信息,从而让spring可以判断是哪个用户在操作。别名是通过”alias name=alias”这样的格式传入的,alias name默认是_s,可以通过setSessionAliasParamName(String)方法修改。我们可以在url上或者表单里添加”_s=your user alias”这样的形式来指明操作用户的别名。如果不指明用户别名,则会认为是默认用户,可以通过setSessionAliasParamName(null)取消别名功能。

public String getCurrentSessionAlias(HttpServletRequest request) {if(sessionParam == null) {return DEFAULT_ALIAS;}String u = request.getParameter(sessionParam);if(u == null) {return DEFAULT_ALIAS;}if(!ALIAS_PATTERN.matcher(u).matches()) {return DEFAULT_ALIAS;}return u;}

触发session提交

spring会通过两个方面确保session提交:

a) response提交,主要包括response的sendRedirect和sendError以及其关联的字节字符流的flush和close方法。

abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {public OnCommittedResponseWrapper(HttpServletResponse response) {super(response);}/** * Implement the logic for handling the {@link javax.servlet.http.HttpServletResponse} being committed */protected abstract void onResponseCommitted();@Overridepublic final void sendError(int sc) throws IOException {doOnResponseCommitted();super.sendError(sc);}//sendRedirect处理类似sendError@Overridepublic ServletOutputStream getOutputStream() throws IOException {return new SaveContextServletOutputStream(super.getOutputStream());}@Overridepublic PrintWriter getWriter() throws IOException {return new SaveContextPrintWriter(super.getWriter());}private void doOnResponseCommitted() {if(!disableOnCommitted) {onResponseCommitted();disableOnResponseCommitted();} else if(logger.isDebugEnabled()){logger.debug("Skip invoking on");}}private class SaveContextPrintWriter extends PrintWriter {private final PrintWriter delegate;public SaveContextPrintWriter(PrintWriter delegate) {super(delegate);this.delegate = delegate;}public void flush() {doOnResponseCommitted();delegate.flush();}
//close方法与flush方法类似}
//SaveContextServletOutputStream处理同字符流
}

onResponseCommitted的实现由子类SessionRepositoryResponseWrapper提供

private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper {private final SessionRepositoryRequestWrapper request;/** * @param response the response to be wrapped */public SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) {super(response);if(request == null) {throw new IllegalArgumentException("request cannot be null");}this.request = request;}@Overrideprotected void onResponseCommitted() {request.commitSession();}}

response提交后触发了session提交。 
b) SessionRespositoryFilter 
仅仅通过response提交时触发session提交并不能完全保证session的提交,有些情况下不会触发response提交,比如对相应资源的访问没有servlet处理,这种情况就需要通过全局filter做保证。

protectedvoiddoFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { //省略//filterChain会在所有filter都执行完毕后调用对应的servlet filterChain.doFilter(strategyRequest, strategyResponse); } finally { //所有的处理都完成后提交session wrappedRequest.commitSession() } https://yq.aliyun.com/articles/57421

转载于:https://my.oschina.net/xiaominmin/blog/1598893

spring-session源码解读 sesion相关推荐

  1. Spring Session - 源码解读

    文章目录 Spring Session 流程图 源码分析 Spring Session 流程图 Spring Session 主要是利用过滤器,偷梁换柱,实现session储存无感知的切换. 源码分析 ...

  2. 【赠书福利】掘金爆火小册同名《Spring Boot源码解读与原理剖析》正式出书了!...

    关注我们丨文末赠书 承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解 ...

  3. 实战:Spring Boot源码解读与原理分析

    承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解读与原理剖析> ...

  4. Spring Session源码解析

    AbstractHttpSessionApplicationInitializer,很明显它是一个初始化的类,它是一个抽象类,可以理解为一个公用的基类,然后看一下onStartup这个方法,最主要的方 ...

  5. Spring:源码解读Spring IOC原理

    2019独角兽企业重金招聘Python工程师标准>>> 一.什么是Ioc/DI? IOC容器:主要是完成了 完成对象的创建和依赖的管理注入等. 先从我们自己设计这样一个视角来考虑: ...

  6. spring beans源码解读之--总结篇

    spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类. org.springframework.beans.ann ...

  7. spring beans源码解读之 ioc容器之始祖--DefaultListableBeanFactory

    spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说, DefaultL ...

  8. 【转载】Spring @Async 源码解读。

    由于工作中经常需要使用到异步操作,一直在使用@Async, 今天抽空学习了一下它的执行原理,刚好看到一篇写的很棒的文章,这里转载过来做个记录,感谢原作者的无私奉献. 原文章链接地址:https://w ...

  9. spring core源码解读之ASM4用户手册翻译之一asm简介

    第一章:ASM介绍 1.1 ASM动机: 程序的分析,生成,转换技术可以应用到许多场景: 1.程序分析,从简单的语法解析到完整的语义分析,可以应用在程序中找到潜在的bug,发现无用的代码,工程代码的逆 ...

最新文章

  1. 重磅:辽宁副省长获中国版诺贝尔奖 !2020未来科学大奖揭晓
  2. weiphp----------图灵机器人存在的bug。
  3. conn.execute参数
  4. vi插入模式下的backspace键和方向键“不正常”使用解决方法
  5. 在 WxHtmlWindow 中调用默认浏览器
  6. 今年怪事特别多 时代盘点09十大奇闻
  7. oracle 挖掘日志,Oracle 日志挖掘(LogMiner)使用详解
  8. ASP.NET Core分布式项目实战(Consent Controller Get请求逻辑实现)--学习笔记
  9. 显示天气 php代码,天气预报查询示例代码
  10. 六招教你快速提升网站交互体验,降低跳出率
  11. 拼多多算法笔试2020
  12. cocos2dx遇到的坑1
  13. SAP License:第三只眼看财务-现金流量表编制
  14. Linux下安装JDK说明
  15. SAP 系统License查看申请及导入
  16. UCINET软件使用简介 ——主菜单功能简介1
  17. 服务器更新维护尚未全部完成,【已开服】11月21日全部服务器更新维护公告
  18. SPSS数据分析-交叉表分析
  19. VoIP 软电话客户端实例
  20. Win10关闭自动调节亮度

热门文章

  1. Android Studio第十九期 - Glin架构
  2. 四个步骤实现层级清晰的导航栏
  3. 服务器拒绝连接(The remote system refused the connection.)
  4. BootStrap 智能表单系列 五 表单依赖插件处理
  5. 交换机复习笔记 广播风暴抑制
  6. 策略模式——Strategy
  7. 解决putty中文乱码并远程访问linux界面功能
  8. #招聘# C++高级攻城师一枚
  9. Mybatis---总结
  10. nginx+FastCGI到底是谁影响超时时间