背景:

  Springboot 2.0 (spring-session-data-redis + spring-boot-starter-web)

需求:

  通过cookies中取到的 sessionid 获取到 session

预期效果:

  @Autowired

  private SessionRepositry sessionRepositry;

  ...

  Session session = sessionRespositry.findById(sessionId);

真实结果: 获取到的session是null, 然而通过 request.getSession(); 可以获取到session, 说明 session是存在的.

问题追踪后发现问题:

  cookie中的sessionId 与 session.getId() 不一样!!!

DEBUG:

  1. 先看一看SpringSession是如何从Cookie中获取sessionid的! (相关类: org.springframework.session.web.http.DefaultCookieSerializer)

  

  2. 再看一看 useBase64Encoding 的值是啥, 首先看默认值

  3. 看看这些配置是在哪里被(赋值)确认的, 一路追踪到 org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration 配置类中

 看看 createDefaultCookieSerializer() 是如何实现的

  4. 从上面可以得出结论, 我们无法 通过配置文件 中 server.servlet.session.** 来配置 useBase64Encoding. 使 cookie中的 sessionid 与 session.getId() 保持一致

  

  5. 期间发现的另一个问题: 虽然 sessionCookieConfig 有httpOnly相关配置, 但这里并未将配置设入 cookieSerializer 中, 导致配置文件中的 server.servlet.session.cookie.httpOnly = false 不起作用

  

解决方案:

  第一种方案:  通过配置 自定义的 CookieSerializer 来指定配置信息(如果觉得麻烦请直接看第二种方案), 如下

  a) 首先因为 SessionCookieConfig 接口中并没有定义 isUseBase64Encoding() 等接口, 导致缺少了部分配置, 所以我 自定义了一个 MySessionCookieConfig 接口继承了 SessionCookieConfig, 并写了一个默认实现 MyDefaultSessionCookieConfig

  1. package com.cardgame.demo.center.config;
  2.  
  3. import javax.servlet.SessionCookieConfig;
  4.  
  5. /**
  6. *
  7. * 补充 SessionCookie 中未定义的配置项
  8. * @author yjy
  9. * 2018-06-15 13:30
  10. */
  11. public interface MySessionCookieConfig extends SessionCookieConfig {
  12.  
  13. String getDomainPattern();
  14.  
  15. void setDomainPattern(String domainPattern);
  16.  
  17. String getJvmRoute();
  18.  
  19. void setJvmRoute(String jvmRoute);
  20.  
  21. boolean isUseBase64Encoding();
  22.  
  23. void setUseBase64Encoding(boolean useBase64Encoding);
  24.  
  25. }

MySessionCookieConfig

  1. package com.cardgame.demo.center.config;
  2.  
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5.  
  6. /**
  7. *
  8. * 涵盖 CookieSerializer 所有配置项
  9. * @author yjy
  10. * 2018-06-15 13:31
  11. */
  12. @Component
  13. @ConfigurationProperties(prefix = "server.servlet.session.cookie")
  14. public class MyDefaultSessionCookieConfig implements MySessionCookieConfig {
  15.  
  16. private String name = "SESSION";
  17. private String path;
  18. private String domain;
  19. private String comment;
  20. private int maxAge = -1;
  21. private String domainPattern;
  22. private String jvmRoute;
  23. private boolean httpOnly = true;
  24. private boolean secure = false;
  25. private boolean useBase64Encoding = false;
  26.  
  27. @Override
  28. public String getDomainPattern() {
  29. return domainPattern;
  30. }
  31.  
  32. @Override
  33. public void setDomainPattern(String domainPattern) {
  34. this.domainPattern = domainPattern;
  35. }
  36.  
  37. @Override
  38. public String getJvmRoute() {
  39. return jvmRoute;
  40. }
  41.  
  42. @Override
  43. public void setJvmRoute(String jvmRoute) {
  44. this.jvmRoute = jvmRoute;
  45. }
  46.  
  47. @Override
  48. public boolean isUseBase64Encoding() {
  49. return useBase64Encoding;
  50. }
  51.  
  52. @Override
  53. public void setUseBase64Encoding(boolean useBase64Encoding) {
  54. this.useBase64Encoding = useBase64Encoding;
  55. }
  56.  
  57. @Override
  58. public String getName() {
  59. return name;
  60. }
  61.  
  62. @Override
  63. public void setName(String name) {
  64. this.name = name;
  65. }
  66.  
  67. @Override
  68. public String getPath() {
  69. return path;
  70. }
  71.  
  72. @Override
  73. public void setPath(String path) {
  74. this.path = path;
  75. }
  76.  
  77. @Override
  78. public String getDomain() {
  79. return domain;
  80. }
  81.  
  82. @Override
  83. public void setDomain(String domain) {
  84. this.domain = domain;
  85. }
  86.  
  87. @Override
  88. public String getComment() {
  89. return comment;
  90. }
  91.  
  92. @Override
  93. public void setComment(String comment) {
  94. this.comment = comment;
  95. }
  96.  
  97. @Override
  98. public boolean isHttpOnly() {
  99. return httpOnly;
  100. }
  101.  
  102. @Override
  103. public void setHttpOnly(boolean httpOnly) {
  104. this.httpOnly = httpOnly;
  105. }
  106.  
  107. @Override
  108. public boolean isSecure() {
  109. return secure;
  110. }
  111.  
  112. @Override
  113. public void setSecure(boolean secure) {
  114. this.secure = secure;
  115. }
  116.  
  117. @Override
  118. public int getMaxAge() {
  119. return maxAge;
  120. }
  121.  
  122. @Override
  123. public void setMaxAge(int maxAge) {
  124. this.maxAge = maxAge;
  125. }
  126. }

MyDefaultSessionCookieConfig

  b) 利用 MyDefaultSessionCookieConfig 携带的配置, 自定义 CookieSerializer Bean

  1. package com.cardgame.demo.center.config;
  2.  
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.beans.BeansException;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.ApplicationContextAware;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
  10. import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
  11. import org.springframework.session.web.http.CookieSerializer;
  12. import org.springframework.session.web.http.DefaultCookieSerializer;
  13. import org.springframework.util.ClassUtils;
  14. import org.springframework.util.ObjectUtils;
  15.  
  16. import javax.servlet.ServletContext;
  17. import javax.servlet.SessionCookieConfig;
  18.  
  19. /**
  20. *
  21. * @author yjy
  22. * 2018-06-08 14:53
  23. */
  24. @Slf4j
  25. @Configuration
  26. @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600, redisNamespace = "center")
  27. public class RedisSessionConfig implements ApplicationContextAware {
  28.  
  29. @Bean
  30. public CookieSerializer cookieSerializer(ServletContext servletContext, MySessionCookieConfig sessionCookieConfig) {
  31. DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
  32. if (servletContext != null) {
  33. if (sessionCookieConfig != null) {
  34. if (sessionCookieConfig.getName() != null)
  35. cookieSerializer.setCookieName(sessionCookieConfig.getName());
  36. if (sessionCookieConfig.getDomain() != null)
  37. cookieSerializer.setDomainName(sessionCookieConfig.getDomain());
  38. if (sessionCookieConfig.getPath() != null)
  39. cookieSerializer.setCookiePath(sessionCookieConfig.getPath());
  40. if (sessionCookieConfig.getMaxAge() != -1)
  41. cookieSerializer.setCookieMaxAge(sessionCookieConfig.getMaxAge());
  42. if (sessionCookieConfig.getDomainPattern() != null)
  43. cookieSerializer.setDomainNamePattern(sessionCookieConfig.getDomainPattern());
  44. if (sessionCookieConfig.getJvmRoute() != null)
  45. cookieSerializer.setJvmRoute(sessionCookieConfig.getJvmRoute());
  46. cookieSerializer.setUseSecureCookie(sessionCookieConfig.isSecure());
  47. cookieSerializer.setUseBase64Encoding(sessionCookieConfig.isUseBase64Encoding());
  48. cookieSerializer.setUseHttpOnlyCookie(sessionCookieConfig.isHttpOnly());
  49. }
  50. }
  51. if (ClassUtils.isPresent(
  52. "org.springframework.security.web.authentication.RememberMeServices",
  53. null)) {
  54. boolean usesSpringSessionRememberMeServices = !ObjectUtils
  55. .isEmpty(this.context
  56. .getBeanNamesForType(SpringSessionRememberMeServices.class));
  57. if (usesSpringSessionRememberMeServices) {
  58. cookieSerializer.setRememberMeRequestAttribute(
  59. SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
  60. }
  61. }
  62. return cookieSerializer;
  63. }
  64.  
  65. private ApplicationContext context;
  66.  
  67. @Override
  68. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  69. this.context = applicationContext;
  70. }
  71. }

RedisSessionConfig

  c) 修改配置文件配置

  

  d) 配置完成后重新启动, 再看 SpringHttpSessionConfiguration 加载的时候, 拿到了我们自定义的 CookieSerializer, 我想要的配置都有了!! 打开浏览器测试通过!!

  第二种方案: 拿到 Cookie 中的 sessionId 后, 手动解码, 再 通过 sessionRespositry.findById(sessionId); 获取session

    a) 解码的方案 从 DefaultSerializer 类中 copy 一个 , 如下:

  1. /**
  2. * Decode the value using Base64.
  3. * @param base64Value the Base64 String to decode
  4. * @return the Base64 decoded value
  5. * @since 1.2.2
  6. */
  7. private String base64Decode(String base64Value) {
  8. try {
  9. byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value);
  10. return new String(decodedCookieBytes);
  11. }
  12. catch (Exception e) {
  13. return null;
  14. }
  15. }

    b) 获取步骤:

      String cookieSessionId = "XXX";

      String sessionId = base64Decode(cookieSessionId);

      Session session = sessionRespositry.findById(sessionId);

    c) 搞定! (此方案未解决 httpOnly 不起效的问题, 如果要解决 httpOnly = false , 请看方案一)

Spring Session产生的sessionid与cookies中的sessionid不一样的问题 && httpOnly 设置不起作用的问题??的更多相关文章

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

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

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

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

  3. Spring Boot集成Spring Data Reids和Spring Session实现Session共享(多个不同的应用共用一个Redis实例)

    从Redis的Key入手,比如Spring Session在注解@EnableRedisHttpSession上提供了redisNamespace属性,只需要在这里设置不同的值即可,效果应该是这样的: ...

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

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

  5. (转)从零开始的Spring Session(一)

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

  6. Session机制详解及分布式中Session共享解决方案

    一.为什么要产生Session http协议本身是无状态的,客户端只需要向服务器请求下载内容,客户端和服务器都不记录彼此的历史信息,每一次请求都是独立的. 为什么是无状态的呢?因为浏览器与服务器是使用 ...

  7. 从零开始的Spring Session(一)

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

  8. Nginx+Tomcat搭建集群,Spring Session+Redis实现Session共享

    小伙伴们好久不见!最近略忙,博客写的有点少,嗯,要加把劲.OK,今天给大家带来一个JavaWeb中常用的架构搭建,即Nginx+Tomcat搭建服务集群,然后通过Spring Session+Redi ...

  9. Cookie中的sessionid与JSONP原理

    一.首先说明一下cookie中的sessionid的作用. 1.cookie只是一些文本内容,多是键值对的形式,是请求头中的一部分 2.http是无连接的 知道这两点,就可以很容易的理解session ...

随机推荐

  1. 第四届 CCCC 团体程序设计天梯赛 游记

    我们可能是唯一一个去参加这个比赛的中学生吧(划掉) DAY -inf 一天教练给我们说有这么个比赛,要选人,于是就愉快的开展了一次打字比赛 说实话手真的要抽筋了 不过最后还好涉险过关 DAY -1 疯 ...

  2. Linux-进程管理

    关于进程 Process what is process ? 什么是进程 process life cycle 进程的生命周期 process states 进程状态 什么是进程? 进程是已启动的可执 ...

  3. spring的事件驱动模型

    在工作中会遇到这样的业务,生成一个订单后需要给指定的用户发送短信或者邮件,但是短信或者邮件发送失败又不会影响正常的业务: 这里介绍通过ApplicationContext和spring的@EventL ...

  4. 079、监控利器 sysdig (2019-04-26 周五)

    参考https://www.cnblogs.com/CloudMan6/p/7646995.html   sysdig 是一个轻量级的系统监控工具,同时他还原生支持容器.通过sysdig我们可以近距离 ...

  5. Windows Server 2016 配置 IIS 的详细步骤

    Ø  简介 本文主要记录 Windows Server 2016 环境下,安装配置 IIS 的详细步骤.需要说明的是,在选择"功能"或"角色服务"时不建议将所有 ...

  6. PHP代码审计之命令注入

    命令注入 命令注入就是通过利用无验证变量构造特殊语句对服务器进行渗透. 注入的种类有很多,而不仅仅是SQL Injection. php常见注入有以下几种(常见:,常见!!): 命令注入 (Comma ...

  7. Ubuntu16.04安装NVIDA驱动和CUDA

    该GPU是计算卡,不会用做显示,所以如果你希望自己的显示使用GPU,本方法可能失效. 服务器配置: CPU: E5-母鸡 GPU: NVIDIA  Tesla K40c 操作系统:Ubuntu 16. ...

  8. 20155324王鸣宇 《网络对抗技术》Web基础

    20155324王鸣宇 <网络对抗技术>Web基础 实践要求 ①Web前端HTML: 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HT ...

  9. jqGrid整合篇

    jqgrid实现多级表头 //使用setGroupHeaders参数: $( "#datagrid" ).jqGrid( 'setGroupHeaders' , { useColS ...

  10. list转换string

    方法1 "".join 若list中为字符型 str = "".join(list) >>> L=['a','b','c'] >> ...