描述:SessionCookieConfig用来修改会话跟踪的cookie的相关信息,包含name,path,domain,以前名称是固定的必须为JSESSIONID,但是通过该接口可以实现自定义。
背景:
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
b) 利用 MyDefaultSessionCookieConfig 携带的配置, 自定义 CookieSerializer Bean
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 , 请看方案一)