背景:

  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

package com.cardgame.demo.center.config;

import javax.servlet.SessionCookieConfig;

/**
*
* 补充 SessionCookie 中未定义的配置项
* @author yjy
* 2018-06-15 13:30
*/
public interface MySessionCookieConfig extends SessionCookieConfig { String getDomainPattern(); void setDomainPattern(String domainPattern); String getJvmRoute(); void setJvmRoute(String jvmRoute); boolean isUseBase64Encoding(); void setUseBase64Encoding(boolean useBase64Encoding); }

MySessionCookieConfig

package com.cardgame.demo.center.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; /**
*
* 涵盖 CookieSerializer 所有配置项
* @author yjy
* 2018-06-15 13:31
*/
@Component
@ConfigurationProperties(prefix = "server.servlet.session.cookie")
public class MyDefaultSessionCookieConfig implements MySessionCookieConfig { private String name = "SESSION";
private String path;
private String domain;
private String comment;
private int maxAge = -1;
private String domainPattern;
private String jvmRoute;
private boolean httpOnly = true;
private boolean secure = false;
private boolean useBase64Encoding = false; @Override
public String getDomainPattern() {
return domainPattern;
} @Override
public void setDomainPattern(String domainPattern) {
this.domainPattern = domainPattern;
} @Override
public String getJvmRoute() {
return jvmRoute;
} @Override
public void setJvmRoute(String jvmRoute) {
this.jvmRoute = jvmRoute;
} @Override
public boolean isUseBase64Encoding() {
return useBase64Encoding;
} @Override
public void setUseBase64Encoding(boolean useBase64Encoding) {
this.useBase64Encoding = useBase64Encoding;
} @Override
public String getName() {
return name;
} @Override
public void setName(String name) {
this.name = name;
} @Override
public String getPath() {
return path;
} @Override
public void setPath(String path) {
this.path = path;
} @Override
public String getDomain() {
return domain;
} @Override
public void setDomain(String domain) {
this.domain = domain;
} @Override
public String getComment() {
return comment;
} @Override
public void setComment(String comment) {
this.comment = comment;
} @Override
public boolean isHttpOnly() {
return httpOnly;
} @Override
public void setHttpOnly(boolean httpOnly) {
this.httpOnly = httpOnly;
} @Override
public boolean isSecure() {
return secure;
} @Override
public void setSecure(boolean secure) {
this.secure = secure;
} @Override
public int getMaxAge() {
return maxAge;
} @Override
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
}

MyDefaultSessionCookieConfig

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

package com.cardgame.demo.center.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import javax.servlet.ServletContext;
import javax.servlet.SessionCookieConfig; /**
*
* @author yjy
* 2018-06-08 14:53
*/
@Slf4j
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600, redisNamespace = "center")
public class RedisSessionConfig implements ApplicationContextAware { @Bean
public CookieSerializer cookieSerializer(ServletContext servletContext, MySessionCookieConfig sessionCookieConfig) {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
if (servletContext != null) {
if (sessionCookieConfig != null) {
if (sessionCookieConfig.getName() != null)
cookieSerializer.setCookieName(sessionCookieConfig.getName());
if (sessionCookieConfig.getDomain() != null)
cookieSerializer.setDomainName(sessionCookieConfig.getDomain());
if (sessionCookieConfig.getPath() != null)
cookieSerializer.setCookiePath(sessionCookieConfig.getPath());
if (sessionCookieConfig.getMaxAge() != -1)
cookieSerializer.setCookieMaxAge(sessionCookieConfig.getMaxAge());
if (sessionCookieConfig.getDomainPattern() != null)
cookieSerializer.setDomainNamePattern(sessionCookieConfig.getDomainPattern());
if (sessionCookieConfig.getJvmRoute() != null)
cookieSerializer.setJvmRoute(sessionCookieConfig.getJvmRoute());
cookieSerializer.setUseSecureCookie(sessionCookieConfig.isSecure());
cookieSerializer.setUseBase64Encoding(sessionCookieConfig.isUseBase64Encoding());
cookieSerializer.setUseHttpOnlyCookie(sessionCookieConfig.isHttpOnly());
}
}
if (ClassUtils.isPresent(
"org.springframework.security.web.authentication.RememberMeServices",
null)) {
boolean usesSpringSessionRememberMeServices = !ObjectUtils
.isEmpty(this.context
.getBeanNamesForType(SpringSessionRememberMeServices.class));
if (usesSpringSessionRememberMeServices) {
cookieSerializer.setRememberMeRequestAttribute(
SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
}
}
return cookieSerializer;
} private ApplicationContext context; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}

RedisSessionConfig

  c) 修改配置文件配置

  

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

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

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

    /**
* Decode the value using Base64.
* @param base64Value the Base64 String to decode
* @return the Base64 decoded value
* @since 1.2.2
*/
private String base64Decode(String base64Value) {
try {
byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value);
return new String(decodedCookieBytes);
}
catch (Exception e) {
return null;
}
}

    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. C# AddRange为数组添加多个元素的代码

    将代码过程中重要的代码片段做个收藏,下面代码段是关于C# AddRange为数组添加多个元素的代码,希望对小伙伴有所用处.ArrayList ab = new ArrayList();ab.Add(& ...

  2. elastic的gc相关

    https://www.jianshu.com/p/1f450826f62e   gc原理介绍 相关优化 https://zhaoyanblog.com/archives/319.html 问题 ht ...

  3. P3709 大爷的字符串题 (莫队)

    题目 P3709 大爷的字符串题 题意:求\([l,r]\)中众数的个数. 解析 维护两个数组: \(cnt[x]\),数\(x\)出现的次数. \(sum[x]\),出现次数为\(x\)的数的个数. ...

  4. request 获取body内容

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException ...

  5. 野路子码农系列(2)Python中的类,可能是最通俗的解说

    啥叫佩奇?啥叫类?啥叫面向对象?后面两个问题以前在大学里“祖传谭浩强”的时候我经常会有所疑问.老师说着一堆什么public, private,我都是一脸懵逼,啥叫私有?为啥要私有?然后就神游天外了…… ...

  6. Tomcat系列(6)——Tomcat处理一个HTTP请求的过程

    Tomcat的架构图   图三:Tomcat Server处理一个HTTP请求的过程 处理HTTP请求过程 假设来自客户的请求为:http://localhost:8080/test/index.js ...

  7. element ui change 传递带自定义参数

    @change="((val)=>{changeStatus(val, index)})" https://www.cnblogs.com/mmzuo-798/p/10438 ...

  8. 作业二Wordcount

    1,github地址 https://github.com/dtneverdie/word-count 2,PSP表格 3,解题思路 先从理论上判断应该先将文件内的字符全部读入,然后根据分隔符来进行单 ...

  9. jQuery循环遍历取值

    1:循环遍历取值 var arr = new Array(); $(".plus-tag a span").each(function(i) { arr[i] = $(this). ...

  10. 初始化仓库(git init)

    创建新的仓库 首先进入需要初始化的目录,然后输入git init D:\Git\test λ git init Initialized empty Git repository in D:/Git/t ...